C++的面向对象编程二

C++的面向对象编程

一、类

1、一旦类定义完成,就没有任何方式可以增加成员了。

 

2、在类内,声明成员函数是必须的,而定义成员函数则是可选的。在类内部定义的函数默认为inline【内联函数】。

 

3、成员函数:

         doubledoSomething() const;

这样将const加在形参表之后,就可以将成员函数声明为常量,其实是限定不得修改this的内容,那么结果就是不得修改所操作对象的数据成员。const必须出现在声明和定义中,缺一不可,否则出现编译时错误。

 

4、this:在普通的非const成员函数中,this的类型是一个指向类类型的const指针!可以改变this所指向的值,但不可以改变this所保存的地址。在const成员函数中,this的类型是一个指向const类类型对象的const指针。既不能改变this所指向的对象,也不能改变this所保存的地址。【两个this,一个指向普通类,一个指向const类,两个同时都是const变量】

 

5、不能从const成员函数返回指向类对象的普通引用。const成员函数只能返回*this作为一个const引用

         eg)

         constClassName& doSome() const{

                   ……

                   return*this;

}

const成员函数,应该返回一个const引用。

 

6、少数情况,使用mutable关键字定义一个可变的成员变量。这样即使是在const成员函数中,也可以改变这个变量。

 

7、类的处理:首先编译成员声明,只有在所有成员出现之后,才编译它们的定义本身。

 

8、构造函数的工作是保证每个对象的数据成员具有合适的初始值。只要创建某类型的一个对象,编译器就运行一个构造函数。每次运行构造函数,都会先初始化成员变量,再进行函数体的计算。也就是说,在函数体进行计算时,是对成员变量的赋值,而非初始化。

 

9、构造函数初始化列表:没有默认构造函数的类类型的成员,以及const,或引用类型的成员,不管那种类型,都必须在构造函数初始化列表中进行初始化。可以初始化const对象或引用类型的对象,但不能对它们赋值。初始化的顺序是按照成员变量的声明顺序进行初始化的。

 

10、友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类。它只能出现在类定义的内部。友元的声明与作用域。对于声明一个友元函数。首先必须先定义一个类A,类A中声明一个函数a(),但是此时还不能定义a()。然后再定义一个B,在B中有private的成员变量,然后在B中声明A::a()为友元。完成B的定义之后,再在类外,定义a(),之所以将a()声明为B的友元,原因是为了使用B中的private变量,所以需要等B定义完。【?疑问,似乎理解错了,这里需要再次验证!?】

 

11、static类成员,类静态成员,为类共享的static数据成员。也可以定义static成员函数,这种成员函数没有this形参,它可以直接访问所属类的static成员,但不能直接使用非static成员。static只需要出现在成员函数的声明,在类外部的定义时无须重复。一般的,static关键字只能用于定义体内部的声明中。

 

12、static成员函数不能被声明为const,因为const是为了不改变this,而static成员函数没有this形参。static成员函数也不能被声明为虚函数。

 

13、一般而言,类的static成员像普通数据成员一样,不能在类的定义体中初始化。相反的static数据成员通常在定义时才初始化。这个规则的一个例外是:只要初始化式是一个常量表达式,整型const static数据成员就可以在类的定义中进行初始化。

 

14、static成员不是类对象的组成部分,独立于任何对象而存在。

 

二、复制控制

1、复制构造函数、赋值操作符、析构函数。

 

2、复制构造函数和默认构造函数一样,可由编译器隐式调用。

eg)对于类类型对象的初始化:

string a = “999-999-999”; //这是一个复制初始化

string b(10,’.’); //这是一个直接初始化

string empty = string(); //这是一个复制初始化

string empty_d; //这是一个直接初始化

         直接初始化直接调用与实参匹配的构造函数。复制初始化则调用复制构造函数。复制初始化首先使用指定的构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象。

 

三、重载操作符与转换

1、不能重新定义用于内置类型对象的操作符的含义。

2、作为成员函数的操作符有一个隐含的this形参,限定为第一个操作数。

3、重载逗号、取地址、逻辑与、逻辑或等操作符通常不是好做法。这些操作符具有有用的内置含义,如果我们定义了自己的版本,就不能在使用这些内置含义了。

 

四、面向对象的编程

1、继承层次的根类一般都要定义虚析构函数。

2、保留字virtual的目的是启用动态绑定。成员默认为非虚函数,对非虚函数的调用在编译时确定。除了构造函数之外,任意非static成员函数都可以是虚函数。virtual保留字只在类内部的成员函数声明中出现。

3、public和private的出现,将类的成员分成了两个阵营,类内自己的调用和类外代码的调用。很明显,在类外,除了友元,其他地方的代码是不能访问对象的private成员的。但自从有了继承,即有了子类,我们希望子类(内部)中可以访问某些成员的同时,外部的代码不可访问,因此有了protected。

4、派生类即子类中,关于虚函数的声明必须与基类即父类完全一致。意思就是,从返回类型,函数名,参数表,包括const都要一致。这里有一个例外,在父类中返回类型是一个对父类的引用或子针。那么在子类中,可以返回一个父类,也可以返回自己的指针。

5、引用和指针的静态类型和动态类型可以不同,这是C++用以支持多态性的基石。

6、如果调用非虚函数,则调用的函数要看指针或引用的声明的类型

eg)

Father *c = new children( i ); 

c->printSome(); //假设还成员函数为非虚函数。则结果为调用Father类定义的函数结果,因为c的声明类型为Father。

如果调用虚函数,则直到运行时才能确定调用哪个函数,运行的虚函数是引用所绑定的或指针所指向的对象所属类型定义的版本。

7、非虚函数总是在编译时根据调用该函数的对象、引用或指针的类型而确定。

8、无论派生列表中是什么访问标号,所有继承A的类对A中的成员具有相同的访问。派生访问标号将控制派生类的用户对从A继承而来的成员的访问。

eg)

class A{

         public:

                   int I;

         protected:

                   int j;

         private:

                   int k;

}

不管是什么访问标号的继承,在派生类B中,都可以使用i和j,但不能使用k。但是在类定义体之外的代码对B中成员的访问就要看访问标号了。如果是public继承,则i,j维持相同的访问级别。那么外部代码只能访问i。如果是protected继承,则i也会变成protected,外部代码连i也不能访问。如果是private继承,则全部来自A的成员在B中都会编程private的,即外部代码无法使用B中继承自A的成员。

9、在给函数传值时,如果形参是一个基类的引用,则在传参时,可以传递一个基类也可以传递一个派生类。如果形参是基类的引用而实参是一个派生类,引用直接绑定到派生类上,派生类对象没有发生变化。然而,如果函数的形参是基类的普通类型,而不是引用,那么这个时候如果实参是一个派生类,则派生类中基类的部分会被复制到形参。但是派生类对象并没有发生变化,只是复制了基类部分传了过去。

一个是引用直接绑定,一个是发生了复制。但是都没有改变派生类,在函数中,由于形参的声明是基类,因此只能调用基类有的东西。

 

转换与继承

1、  每个派生类对象包含一个基类部分。这意味着可以像操作基类一样操作派生类。

2、  存在从派生类型引用 -> 基类类型引用 的自动转换。对指针也类似。

3、  基类对象可以独立存在,也可以作为派生类的一部分存在,因此无法确定基类的存在状态,所以不存在从基类类型引用到派生类类型引用的自动转换。

4、  上述所说的都是引用和指针之间的转换,并不涉及对象的转换。

5、  一般,可以使用派生类对象去初始化或者赋值基类对象,但,没有从派生类对象到基类对象的直接转换。

派生类->基类

1、  如果有一个派生类对象,则可以使用它的地址对基类类型的指针进行赋值或初始化。

2、  同样可以使用派生类型的引用或对象初始化基类类型的引用

3、  用派生类对象对基类对象进行初始化或赋值时:

a)        首先派生类对象转换为基类类型的引用。实际上则是将一个基类的引用绑定到派生类对象上

b)        将该引用作为实参传给复制构造函数或赋值操作符。

c)        使用派生类对象中基类的部分对目标进行初始化或赋值。该过程忽略了派生类对象中派生类特有的数据。

d)        初始化或复制结束后,得到的对象为基类类型的对象。

基类->派生类

1、  从基类到派生类的自动转换时不存在的。

 

继承中的构造函数

1、  派生类的构造函数调用前会先隐式调用基类的构造函数。

2、  应该在派生类的构造函数的初始化列表上,指出要怎么初始化基类。尊重基类的构造函数。

3、  每个派生类都只能初始化自己的直接基类。

 

继承中的复制控制

1、  应当在派生类的复制构造函数中的初始化列表上,显式调用基类的复制构造函数,否则生成的派生类对象的基类部分将保存默认值,而派生类部分则是一个对象的副本。

2、  应当在派生类的赋值操作符的函数体内,显式调用基类的赋值操作符。

3、  派生类析构函数不负责撤销基类对象的成员。编译器会自动地按照继承层次的逆序去调用各个类的析构函数。

4、  基类的析构函数一般定义为虚函数。而如此做了,它的所有派生类的析构函数都将是虚函数。即使析构函数没有工作要做,继承层次的根类也应该定义一个虚析构函数。

 

纯虚函数

eg)

ReturnType funcName( Type, Type … ) const =0;

 

继承中的句柄类

C++中面向对象编程的一个颇具讽刺意味的地方是,不能使用对象支持面向对象编程,相反,必须使用指针或引用。

因此为了解决这个问题,我们定义一个句柄类,存储和管理基类指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值