继承概念:
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持
原有类特性的基础上进行扩展,增加功能。这样产生新的类,称派生类。继承呈现了面向对象程序设
计的层次结构,体现了由简单到复杂的认知过程 。
class Base { public: Base() { cout<<"B()" <<endl; } ~Base () { cout<<"~B()" <<endl; } void ShowBase() { cout<<"_pri = " <<_pri<< endl; cout<<"_pro = " <<_pro<< endl; cout<<"_pub = " <<_pub<< endl; } private: int _pri; protected: int _pro; public: int _pub; }; class Derived:public Base { public: Derived() { cout<<"D()"<<endl; } ~Derived () { cout<<"~D()"<<endl; } void ShowDerived() { _d_pri = _pri;// 这个赋值语句是错误的,公有继承中基类的私有成员是不能访问的 _d_pro = _pro; _d_pub = _pub; cout<<"_d_pri = "<<_d_pri<< endl; cout<<"_d_pro = "<<_d_pro<< endl; cout<<"_d_pub = "<<_d_pub<< endl; } private: int _d_pri; protected: int _d_pro; public: int _d_pub; };
可以看出在派生类公有继承基类之后派生类只可以访问基类的公有成员和保护成员,私有成员不能访问
总结:
1. 基类的 private 成员在派生类中是不能被访问的,如果基类成员不想在类外直接被访问,但需要
在派生类中能访问,就定义为 protected 。可以看出保护成员限定符是因继承才出现的。
2. public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类
对象也都是一个父类对象。
3. protected/private继承是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,
是 has-a 的关系原则,所以非特殊情况下不会使用这两种继承关系,在绝大多数的场景下使用的
都是公有继承。私有继承以为这is-implemented-in-terms-of(是根据……实现的)。通常比
组合(composition)更低级,但当一个派生类需要访问基类保护成员或需要重定义基类的虚函
数时它就是合理的。
4. 不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,基类的私有成员存
在但是在子类中不可见(不能访问)。
5. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最
好显示的写出继承方式。
6. 在实际运用中一般使用都是public继承,极少场景下才会使用protetced/private继承.
在派生类中能访问,就定义为 protected 。可以看出保护成员限定符是因继承才出现的。
2. public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类
对象也都是一个父类对象。
3. protected/private继承是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,
是 has-a 的关系原则,所以非特殊情况下不会使用这两种继承关系,在绝大多数的场景下使用的
都是公有继承。私有继承以为这is-implemented-in-terms-of(是根据……实现的)。通常比
组合(composition)更低级,但当一个派生类需要访问基类保护成员或需要重定义基类的虚函
数时它就是合理的。
4. 不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,基类的私有成员存
在但是在子类中不可见(不能访问)。
5. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最
好显示的写出继承方式。
6. 在实际运用中一般使用都是public继承,极少场景下才会使用protetced/private继承.
继承关系中构造函数和析构函数调用顺序
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
cout<<"基类构造函数被调用"<<endl;
}
~Base()
{
cout<<"基类析构函数被调用"<<endl;
}
};
class Derived:public Base
{
public:
Derived()
:Base()
{
cout<<"子类构造函数被调用"<<endl;
}
~Derived()
{
cout<<"子类析构函数被调用"<<endl;
}
};
int main()
{
Derived b1;
return 0;
}
在这我们可以看出来先调用基类的构造函数然后在调用派生类的构造函数,在程序退出的时候西安调用派生类的
析构函数在调用基类的析构函数。其实不然,真正的调用顺序是:在定义派生类对象 b1 时先调用派生类的构造函数在
派生类的初始化列表中调用了基类的构造函数,在基类的构造函数完成之后在返回来继续调用派生类的构造函数,而
析构函数则是直接调用派生类的在派生类的析构函数将要完成时(就是右花括弧)之前在调用基类的析构函数,最后基类
的析构函数完成之后返回在继续派生类的析构函数。
继承体系中的作用域:
1. 在继承体系中基类和派生类是两个不同作用域。
2. 子类和父类中有同名成员,子类成员将屏蔽父类对成员的直接访问。(在子类成员函数中,可以使用 基类::基类成员 访问)
3. 注意在实际中在继承体系里面最好不要定义同名的成员。
2. 子类和父类中有同名成员,子类成员将屏蔽父类对成员的直接访问。(在子类成员函数中,可以使用 基类::基类成员 访问)
3. 注意在实际中在继承体系里面最好不要定义同名的成员。
继承与转换--赋值兼容规则--public继承:
1. 子类对象可以赋值给父类对象(切割/切片)
2. 父类对象不能赋值给子类对象
3. 父类的指针/引用可以指向子类对象
4. 子类的指针/引用不能指向父类对象(可以通过强制类型转换完成)
1. 子类对象可以赋值给父类对象(切割/切片)
2. 父类对象不能赋值给子类对象
3. 父类的指针/引用可以指向子类对象
4. 子类的指针/引用不能指向父类对象(可以通过强制类型转换完成)
友元与继承
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。
继承与静态成员
基类定义了static成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成
员实例。
基类定义了static成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成
员实例。
单继承&多继承&菱形继承
【单继承】
一个子类只有一个直接父类时称这个继承关系为单继承。
【单继承】
一个子类只有一个直接父类时称这个继承关系为单继承。
【多继承】
一个子类有两个或以上直接父类时称这个继承关系为多继承
一个子类有两个或以上直接父类时称这个继承关系为多继承
【菱形继承】
class A
{
public:
A()
{
_a = 5;
cout<<"A _a="<<_a<<endl;
}
int _a;
};
class B1: public A
{
public:
B1()
{
_a = _a+10;
cout<<"B1 _a="<<_a<<endl;
}
};
class B2: public A
{
public:
B2()
{
_a = _a+20;
cout<<"B2 _a="<<_a<<endl;
}
};
class c:public B2, public B1
{
public:
c()
{
cout<<"c _a="<<_a<<endl;
}
};
int main()
{
c c1;
return 0;
}
B1和B2分别公有继承了A此时C又公有继承B1和B2此时C类要访问基类A的成员 _a 编译器会报错_a的访问不明确因为它不知道类C要访问的是B1类继承基类的_a还是B2类继承基类的_a,所以他就会产生二义性。如果要访问_a有一下两种方法:
1、显示的指定访问那个父类的成员。
class A
{
public:
A()
{
_a = 5;
cout<<"A _a="<<_a<<endl;
}
int _a;
};
class B1: public A
{
public:
B1()
{
_a = _a+10;
cout<<"B1 _a="<<_a<<endl;
}
};
class B2: public A
{
public:
B2()
{
_a = _a+20;
cout<<"B2 _a="<<_a<<endl;
}
};
class c:public B2, public B1
{
public:
c()
{
cout<<"B1 _a="<<B1::_a<<endl;
cout<<"B2 _a="<<B2::_a<<endl;
}
};
int main()
{
c c1;
return 0;
}
虚继承解决了在菱形继承体系里面子类对象包含多份父类对象的数据的二义性的问题
class A
{
public:
int _a;
};
class B1:virtual public A
{
public:
int _b1;
};
class B2:virtual public A
{
public:
int _b2;
};
class C:public B1, public B2
{
public:
int _c;
};
int main()
{
C c1;
c1._a = 1;
c1._b1 = 2;
c1._b2 = 3;
c1._c = 4;
return 0;
}