下面介绍我对C++语法继承的总结:
1.什么是继承?
继承:通过一种机制来表达类型之间共性和特性的方法,利用自己已经拥有的特性来定义新的数据类型。这种机制成为类型。
保护继承 protected
私有继承 private
被继承的类称为基类,继承其他类的类叫做子类(派生类)
3.语法形式: class 子类名:继承方式 基类名{};
如:
class A{
public:
int m1;
int m2;
}
class B:public A{};
上面B类就继承了A类,我们能用B类创建对象 B b;并调用b.m1或b.m2
4.向上造型
将子类的指针或者引用转换为基类的指针或者引用使用。-->子类的指针的作用域为整个子类,子类继承了基类。所以可以理解为子类作用域>=基类作用域。
所以将子类的指针或者引用转换为基类的指针或者引用缩小了指针的作用范围,是可以的。
eg:
B *p;
A *p1 = &p;//ok
将基类类型的指针或者引用转换为子类的指针或者引用使用。
-->基类的指针作用域为整个基类。子类继承了基类。而且子类还可以有自己的成员变量或函数,所以子类的指针所指向的范围包含基类且可能比基类大。所以这种将基类的指针转换为子类的指针的做法是错误的!
eg:
A *q;
B *q1 = &q;//error
-->在子类中,可以直接访问基类中的public成员,就如同访问自己的成员一样。没有限制。
-->基类的私有成员也是可以继承过来的,但是收到访问控制限定符限制,子类不能直接访问,但是可以通过基类中的保护函数或者公有函数访问。
-->基类中的构造函数和析构函数,子类不会继承。
--> 如果子类中有函数名字和基类中的函数名字一样(就算参数不一样),那么他们会被子类隐藏,外部访问时会直接调用子类自己的函数。要想访问基类中的函数可以加上访问限定符 类名::函数名 来访问
访问控制限定符:影响访问该类成员的位置
class B:public/protected/private A{};
-------------------------------------------------------------------
访问控制限定符 访问控制属性 内部访问 外部访问 子类访问 有元访问
public: 共有 ok ok ok ok
protected: 保护 ok no ok ok
private: 私有 ok no no ok
-------------------------------------------------------------------
通过子类访问基类中成员的可访问性
-------------------------------------------------------------------
基类中的 共有子类中 保护子类 私有子类
公有成员 共有 保护 私有
保护成员 保护 保护 私有
私有成员 私有 私有 私有
-------------------------------------------------------------------
注:向上造型在私有和保护继承时,不再适用。
1)如果子类的构造函数没有指明基类子对象的初始化方式,那么将会调用基类的无参构造函数来初始化该子对象。
2)如果想要基类子对象以有参的方式初始化,那么必须在子类构造函数中指明 语法: 基类(参数)
eg: B(int i,int j):A::m1(i),A::m2(j){}
3)子类对象创建的过程:
-->分配内存
-->构造基类对象(按继承表的顺序)
-->构造成员子对象(按声明顺序)
-->执行子类的构造函数
子对象(如果子对象里还有其他类申明的对象)创建时会先执行基类的构造函数,在构造声明的其他类的构造函数,在执行自己的构造函数
子对象的销毁过程(含有其他类定义的对象时)
-->执行子对象的析构函数
-->执行其他类的析构函数(声明逆序)
-->执行基类的析构函数(继承表逆序)
-->释放内存
注:基类的析构函数不胡调用子类的析构函数。如果对一个指向子对象的基类的指针使用"delete"操作符,实际被执行的仅仅是基类的析构函数。
-->如果子类没有定义拷贝构造函数,编译器会为子类提供缺省的拷贝构造函数,该函数会自动调用基类的拷贝构造函数,初始化基类子对象。
-->子类定义了拷贝构造函数,需要使用初始化表指明基类子对象的初始化方式 ,初始化表中使用:基类名(要拷贝的对象)
-->如果子类没有定义拷贝赋值函数,那么编译器会为子类提供缺省的拷贝赋值函数,该函数会自动调用基类的拷贝赋值操作符函数,复制基类子对象。
-->如果子类定义了拷贝赋值函数,需要显示的调用 Base::operator=(that) 基类的拷贝赋值构造函数完成对基类对象的复制
12.多重继承
一个子类同事继承多个基类
1.class 子类:public 基类1,public 基类2,...{};
多重继承在做向上造型时,编译器会根据各个基类子对象的内部布局,进行适当的便宜计算,保证指针的类型和所指向的目标对象类型一致。
2.函数名字冲突解决办法:
当一个子类继承多个基类是,在多个积累中如果存在名字相同的成员函数,子类对象去调用他们时,就会报歧义语法错误。解决办法:
通过 类名::来显式指明该函数具体属于哪一个类。或者使用 using::(不推荐)。
13. 钻石继承
概念:一个子类的多个基类又属于同一个基类(父类的父类)
-->公共基类子对象,在汇聚的子类对象中存在多个实例(会被创建多次)。如A类中有int num; B,C类是A的子类。D类又继承BC类。那么定义一个D类对象时,会在B类 和C类中都创建一个num,所以你通过D类访问的num 是不确定的。
解决办法:
-->通过虚继承可以让公共基类子对象(num)在他下面的所有子类中(BCD)创建的实例是唯一的。
语法:
-->在继承表中用 virtual 声明(在B,C的继承表前加vitual,加载继承方式前面)在D的初始化构造函数中要加上A的初始化方式(BC也要,否则编译器会以无参方式去初始化基类子对象)。