父类/子类
基类/派生类
class 派生类 : [访问限定符] 基类 {
成员
}
注:如果不写继承访问限定符,默认是private
public | protected | private | |
---|---|---|---|
类成员函数 | √ | √ | √ |
友元函数 | √ | √ | √ |
子类函数 | √ | √ | × |
类对象 | √ | × | × |
1、子类继承了父类所有的成元变量和成员函数。与访问限定符无关。访问限定符只是限制了访问。
2、子类访问父类成员变量,把父类成员变量访问限制符,改为protected
。
- 实例:
#include
using namespace std;
class Base{
public:
void public_func(){
cout << __func__ <<endl;
}
//类内访问
void Test(){
public_func();
protected_func();
private_func();
}
protected:
void protected_func(){
cout << __func__ << endl;
}
private:
void private_func(){
cout << __func__ << endl;
}
};
class Derive:public Base{
public:
//对于public继承,子类内部可以访问父类public和protected成员
void Func(){
public_func();
protected_func();
//private_func();
}
};
int main(){
Base b;
b.Test();
//对象访问
b.public_func();
//b.protected_func();
//b.private_func();
cout << "Derive:" << endl;
Derive d;
d.Func();
d.public_func();
//d.protected_func();
//d.private_func();
}
public_func
protected_func
private_func
public_func
Derive:
public_func
protected_func
public_func
继承访问权限变化
分为子类内部和子类对象两种访问方式。
1、子类内部访问:
子类内部访问public
继承的父类成员变量
子类内部访问public
继承的父类成员函数
2、子类对象访问:
子类对象访问public
继承的父类成员变量
子类对象访问public
继承的父类成员函数
实例:
#include
using namespace std;
class Base {
public:
int public_data = 1;
void public_func(){};
protected:
int protected_data = 2;
void protected_func(){};
private:
int private_data = 3;
void private_func(){};
};
class Derive:public Base {
public:
//子类内部访问public继承的父类成员函数
void test() {
public_func();
protected_func();
//private_func(); //无法访问
}
//子类内部访问public继承的父类成员变量
void test2() {
cout<< public_data << endl;
cout<< protected_data << endl;
//cout<< private_data <
}
};
int main(){
Derive d;
d.test();
d.test2();
//子类对象访问
//只有public继承时,子类对象才能访问父类的public成员函数和成员变量,其他均不能访问
d.public_func();
//d.protected_func();
//d.private_func();
cout << d.public_data << endl;
//cout << d.protected_data << endl; //无法访问
//cout << d.private_data << endl; //无法访问
}
1
2
1
结论:
1、子类内部访问父类成员(成员函数和成员变量)
public | protected | private | |
---|---|---|---|
public 继承 | √ | √ | × |
protected 继承 | √ | √ | × |
private 继承 | √ | √ | × |
2、子类对象访问父类成员(成员函数和成员变量)
public | protected | private | |
---|---|---|---|
public 继承 | √ | × | × |
protected 继承 | × | × | × |
private 继承 | × | × | × |
结论:子类对象只有public
继承父类的时候,才能访问父类的public
成员,其他都不能访问。
注:通常子类使用public
继承父类。
子类对象访问父类成员访问限定符的变化
继承方式\父类成员 | public | protected | private |
---|---|---|---|
public 继承 | public | protected | 不可见 |
protected 继承 | protected | protected | 不可见 |
private 继承 | private | private | 不可见 |
public
继承
protected
继承
private
继承
小结:
public
无论类内部还是类对象都可以访问。
protected
类对象不可访问,类内部与继承类的内部可以访问。
private
只有类内部可以访问。
- 派生类的构造函数与析构函数的调用顺序
1、派生类的构造函数调用顺序
父对象构造、成员变量构造、子对象构造的顺序
2、派生类的析构函数调用顺序
子对象析构、成员变量析构、父对象析构的顺序
实例:父类、子类和子类成员的构造顺序
#include
using namespace std;
class Member{
public:
Member(){
cout << __func__ << endl;
}
~Member(){
cout << __func__ << endl;
}
};
class Parent{
public:
Parent(){
cout << __func__ << endl;
}
~Parent(){
cout << __func__ << endl;
}
};
class Son:public Parent{
Member m;
public:
Son(){
cout << __func__ << endl;
}
~Son(){
cout << __func__ << endl;
}
};
int main(){
Son s;
}
Parent
Member
Son
~Son
~Member
~Parent
实例2:类成员变量和类的构造顺序
#include
using namespace std;
//组合
class Member{
public:
Member(){
cout << __func__ << endl;
};
~Member(){
cout << __func__ << endl;
}
};
class Member2{
public:
Member2(){
cout << __func__ << endl;
};
~Member2(){
cout << __func__ << endl;
}
};
class Member3{
public:
Member3(){
cout << __func__ << endl;
};
~Member3(){
cout << __func__ << endl;
}
};
class Simple{
// 成员的定义顺序
Member m;
Member2 m2;
Member3 m3;
public:
// Simple(){
//Simple():m(),m2(),m3(){
Simple():m3(),m2(),m(){ // 初始化列表不决定初始化顺序,初始化顺序与成员定义顺序有关
cout << __func__ << endl;
};
~Simple(){
cout << __func__ << endl;
}
};
int main(){
Simple d;
}
Member
Member2
Member3
Simple
~Simple
~Member3
~Member2
~Member
3.4 同名隐藏
概念:子类的成员函数与基类成员函数同名,子类的函数将会隐藏基类的所有同名函数。
实例:同名隐藏
#include
using namespace std;
class Base{
public:
Base(){
cout << __func__ << endl;
}
~Base(){
cout << __func__ << endl;
}
void Test(){
cout << "Base::Test" << endl;
}
void Test(int n){
cout << "Base::Test(" << n << ")" << endl;
}
};
class Derive:public Base{
public:
Derive(){
cout << __func__ << endl;
}
~Derive(){
cout << __func__ << endl;
}
void Test(){
cout << "Derive::Test" << endl;
}
void Test(int n){
cout << "Derive::Test(" << n << ")" << endl;
}
};
int main(){
Derive d;
d.Test();
d.Test(100);
}
Base
Derive
Derive::Test
Derive::Test(100)
~Derive
~Base
解决方法:
1、在Derive
类对象调用被隐藏的父类函数时,在函数前面加上父类限定符。例如:
d.Base::Test(); // 解决同名隐藏第一种方法
2、Derive
类中的名称会隐藏Base
类中同名的名称,在public
继承中我们可以通过引入using
声明。
class Derive:public Base{
public:
using Base::Test;
Derive(){ //Derive():Base(){}
cout << __func__ << endl;
};
~Derive(){
cout << __func__ << endl;
}
void Test(){ // 同名隐藏(只要父类相同名字的成员都被隐藏)
cout << "Derive::Test" << endl;
}
};
实例:
#include
using namespace std;
class Base{
public:
Base(){
cout << __func__ << endl;
};
~Base(){
cout << __func__ << endl;
}
void Test(){
cout << "Base::Test" << endl;
}
void Test(int n){
cout << "Base::Test(" << n << ")" << endl;
}
};
class Derive:public Base{
public:
using Base::Test;
Derive(){ //Derive():Base(){}
cout << __func__ << endl;
};
~Derive(){
cout << __func__ << endl;
}
void Test(){ // 同名隐藏(只要父类相同名字的成员都被隐藏)
cout << "Derive::Test" << endl;
}
};
int main(){
Derive d;
d.Base::Test(); // 解决同名隐藏第一种方法
// d.Base::Test(10);
d.Test(10); // 解决同名隐藏的第二种方法
}
Base
Derive
Base::Test
Base::Test(10)
~Derive
~Base
3.5 函数同名的情况
名称 | 英语 | 作用域 | 形参 | virtual |
---|---|---|---|---|
重载 | overload | 相同的范围(在同一个类中) | 函数名相同,参数列表不同 | virtual 关键字可有可无 |
重写(覆盖) | override | 不同的范围,分别位于基类和派生类中 | 函数名相同 ,参数列表相同 | 基类函数必须有virtual 关键字 |
隐藏 | hide | 不同的范围,分别位于基类和派生类中 | 函数名相同,参数列表可同可不同 | (1)如果派生类的函数和基类的函数同名,但是参数不同,此时,不管有无virtual ,基类的函数被隐藏 (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字,此时,基类的函数被隐藏 |
- 赋值兼容规则
概念:在任何需要基类对象的地方都可以使用公有的派生类对象来代替。反之,不可。
三种情况
1、派生类的对象可以赋值给基类对象
Base base;
Derive derive;
base = derive;
对象切割(Object Slicing
):在赋值时舍弃派生类自己的成员,只进行基类数据成员的赋值
2、派生类的对象可以初始化基类的引用
Derive derive;
Base& base = derive;
3、派生类的对象可以赋值给基类的指针
指向基类对象的指针变量可以指向派生类对象。
Derive derive;
Base* base = &derive;
实例:
#include
using namespace std;
class Base{
protected:
int m;
int n;
public:
Base(int n,int m):n(n),m(m){
cout << __func__ << endl;
};
~Base(){
cout << __func__ << endl;
}
void Test(){
cout << "Base::Test(" << n << "," << m << ")"<< endl;
}
void Test(int n){
cout << "Base::Test(" << n << ")" << endl;
}
void Func(){
cout << "Base:Func" << endl;
}
};
class Derive:public Base{
float f;
public:
using Base::Test;
Derive(int n,int m,float f):Base(n,m),f(f){ //Derive():Base(){}
cout << __func__ << endl;
};
~Derive(){
cout << __func__ << endl;
}
void Test(){ // 同名隐藏(只要父类相同名字的成员都被隐藏)
cout << "Derive::Test(" << n << "," << m << "," << f << ")"<< endl;
}
void Func(){
Base::Test();
//Base::Test(10);
Test(10);
}
};
void Func(Base& b){
b.Test();
}
int main(){
Derive d(1,2,3.14);
d.Test(); //调用子类Test()
d.Func();
Func(d); //调用父类Test()
//派生类的对象可以初始化基类的引用
Base& b = d;
b.Test();
b.Test(10);
cout << &d << ":" << &b << endl;
//派生类的对象可以赋值给基类的指针
Base* p = &d;
p->Test();
p->Test(20);
//决定调用哪个函数的是类型
//派生类的对象可以赋值给基类对象
Base b2 = d;
b2.Test();
}
Base
Derive
Derive::Test(1,2,3.14)
Base::Test(1,2)
Base::Test(10)
Base::Test(1,2)
Base::Test(1,2)
Base::Test(10)
0x7ffde44fa914:0x7ffde44fa914
Base::Test(1,2)
Base::Test(20)
Base::Test(1,2)
~Base
~Derive
~Base
实例:virtual
如果调用函数是virtual
,查看指针/引用指向对象类型中,是否有完全相同的函数,如果有调用指向对象中的函数,否则调用父类函数
#include
using namespace std;
class Base{
public:
Base(){
cout << __func__ << endl;
};
~Base(){
cout << __func__ << endl;
}
virtual void Test(){
cout << "Base::Test" << endl;
}
};
class Derive:public Base{
public:
using Base::Test;
Derive(){ //Derive():Base(){}
cout << __func__ << endl;
};
~Derive(){
cout << __func__ << endl;
}
void Test(){ // 同名隐藏(只要父类相同名字的成员都被隐藏)
cout << "Derive::Test" << endl;
}
};
int main(){
Derive d;
Base& b = d;
b.Test();//父类Test()为虚函数,调用子类Test()函数
//1.对象类型决定调用函数
//2.如果调用函数是virtual,查看指针/引用指向对象类型中,是否有完全相同的函数,如果有调用指向对象中的函数,否则调用父类函数
}
Base
Derive
Derive::Test
~Derive
~Base
实例2:virtual
如果类中定义了虚函数,一定定义虚析构函数,否则会出现子类无法析构
#include
using namespace std;
class Base{
public:
Base(){
cout << __func__ << endl;
};
virtual ~Base(){ //如果类中定义了虚函数,一定定义虚析构函数,否则会出现子类无法析构
cout << __func__ << endl;
}
virtual void Test(){ //虚函数
cout << "Base::Test" << endl;
}
};
class Derive:public Base{
public:
using Base::Test;
Derive(){ //Derive():Base(){}
cout << __func__ << endl;
};
~Derive(){
cout << __func__ << endl;
}
void Test(){ // 同名隐藏(只要父类相同名字的成员都被隐藏)
cout << "Derive::Test" << endl;
}
};
int main(){
Base* p = new Derive;
p->Test();
delete p;
}
Base
Derive
Derive::Test
~Derive
~Base
实例3:virtual
类中声明虚函数时,则会默认构造一个虚函数表指针,虚函数表是一个virtual
函数指针数组
#include
using namespace std;
class Base{
// void* vtpr //类中声明虚函数时,则会默认构造一个虚函数表指针,虚函数表是一个virtual函数指针数组
int n;
public:
Base(){
cout << __func__ << endl;
};
virtual ~Base(){ //如果类中定义了虚函数,一定定义虚析构函数,否则会出现子类无法析构
cout << __func__ << endl;
}
virtual void Test(){ //虚函数
cout << "Base::Test" << endl;
}
};
int main(){
Base b;
cout << sizeof(b) << endl; //8字节+4字节+补齐 = 16字节
}
3.6 多重继承
一个类可以同时继承多个父类的行为和特征功能。
逗号分割的基类列表
语法:
class 类名 : public 基类1,public 基类2{
};
3.7 菱形继承/钻石继承
概念:两个子类继承同一个父类,而又有子类同时继承这两个子类。
实例:等腰直角三角形继承等腰三角形与直角三角形
#include
#include
using namespace std;
//三角形
class Triangle{
protected: //允许子类访问,不允许对象访问
int a,b,c;
public:
Triangle():a(0),b(0),c(0){} //初始化成员变量
Triangle(int a,int b,int c):a(a),b(b),c(c){} //构造函数
int GetLength() const {
return a+b+c;
}
float GetArea(){
float q = GetLength()/2.0;
return sqrt(q*(q-a)*(q-b)*(q-c));
}
};
//等边三角形
class EqualTriangle:public Triangle{
public:
/*
EqualTriangle(int side){
a = b = c = side;
}
*/
EqualTriangle(int a):Triangle(a,a,a){}
};
//等腰三角形
class IsoTriangle:public virtual Triangle{
public:
//IsoTriangle(int side,int iso):Triangle(side,iso,iso){}
//如果父类没有默认构造函数,子类初始化必须调用父类构造函数
//子类的构造函数初始化列表只能初始化自身的成员变量,不能直接初始化父类成员变量
IsoTriangle(int side,int iso){
a = side;
b = iso;
c = iso;
}
//直接使用父类成员变量
//继承只代表拥有,不代表可以访问
};
//直角三角形
class RightAngleTriangle:public virtual Triangle{
public:
RightAngleTriangle(int a,int b,int c):Triangle(a,b,c){}
};
//等腰直角三角形
//等腰三角形与直角三角形形成虚继承关系,四者形成菱形关系
class IsoRightAngleTriangle:public IsoTriangle,public RightAngleTriangle{
public:
IsoRightAngleTriangle(int side,int iso):IsoTriangle(side,iso),RightAngleTriangle(side,iso,iso){}
};
void Print(Triangle t){
cout << t.GetLength() << "\t" << t.GetArea() << endl;
}
void Print2(Triangle& t){
cout << t.GetLength() << "\t" << t.GetArea() << endl;
}
void Print3(Triangle* t){
cout << t->GetLength() << "\t" << t->GetArea() << endl;
}
int main(){
int a,b,c;
cin >> a >> b >> c;
Triangle t(a,b,c);
cout << t.GetLength() << "\t" << t.GetArea() << endl;
IsoTriangle iso(a,b);
cout << iso.GetLength() << "\t" << iso.GetArea() << endl;
Triangle t2 = iso;
cout << t2.GetLength() << "\t" << t2.GetArea() << endl;
Print(t);
Print(iso); //Triangle t = iso;
Print2(t);
Print2(iso); //Triangle& t = iso;
Print3(&t);
Print3(&iso); //Triangle* t = &iso;
//赋值兼容原则,任何使用父类对象的地方,都可以使用public继承的子类对象代替
Triangle& f = iso;
Triangle* p = &iso;
cout << "------------" << endl;
Print2(f);
Print3(p);
cout << "------------" << endl;
Triangle* arr[] = {
new Triangle(2,3,4),
new IsoTriangle(3,5),
new EqualTriangle(3),
new IsoRightAngleTriangle(sqrt(2),1),
};
//赋值兼容原则
for(int i = 0;i < 4;++i){
Print3(arr[i]);
}
}
2 3 4
9 2.90474
8 2.82843
8 2.82843
9 2.90474
8 2.82843
9 2.90474
8 2.82843
9 2.90474
8 2.82843
------------
8 2.82843
8 2.82843
------------
9 2.90474
13 7.15454
9 3.89711
3 0.433013
3.8 关于多重继承
1、什么是多重继承?同时继承多个父类。
2、多重继承有什么危害?菱形继承/钻石继承。
3、什么是菱形继承/钻石继承?多重继承的两个或多个父类具有相同的祖先类。
4、菱形继承/钻石继承有什么危害?因为多重继承的两个或多个父类具有相同的祖先类。所以会有完全相同的属性和方法。因此当前多重继承类有两份相同的属性和方法。使用时会出现冲突。
5、如何解决菱形继承/钻石继承导致的冲突?使用虚继承。
6、什么是虚继承?父类在继承具有相同的祖先类时,加上virtual
.
解决钻石继承的危害:虚继承&虚基类
虚继承:在继承定义中包含了virtual
关键字的继承关系。
虚基类:在虚继承体系中的通过virtual
继承而来的基类。
语法:
class 类名:public virtual 基类{
}
虚基类是一个相对概念,在虚继承关系中,父类相对与子类是虚基类。
3.9 对象构造顺序- 基本原则
先父后子
从左到右
先虚后实
从上到下
由内及外
实例:
#include
using namespace std;
#define SIMPLE_CLASS(name)\
class name{\
public:\
name(){ cout << #name << " Constructor" << endl;}\
~name(){ cout << #name << " Destructor" << endl;}\
};
SIMPLE_CLASS(Base1)
SIMPLE_CLASS(Base2)
SIMPLE_CLASS(Base3)
SIMPLE_CLASS(VBase1)
SIMPLE_CLASS(VBase2)
SIMPLE_CLASS(VBase3)
SIMPLE_CLASS(Member1)
SIMPLE_CLASS(Member2)
SIMPLE_CLASS(Member3)
#undef SIMPLE_CLASS
class Test : public Base1,
public Base2,
public Base3,
public virtual VBase1,
public virtual VBase2,
public virtual VBase3 {
public:
Test() { cout << "Test Constructor" << endl; }
~Test() { cout << "Test Destructor" << endl; }
private:
Member1 m1;
Member2 m2;
Member3 m3;
};
int main() {
Test t;
}
VBase1 Constructor
VBase2 Constructor
VBase3 Constructor
Base1 Constructor
Base2 Constructor
Base3 Constructor
Member1 Constructor
Member2 Constructor
Member3 Constructor
Test Constructor
Test Destructor
Member3 Destructor
Member2 Destructor
Member1 Destructor
Base3 Destructor
Base2 Destructor
Base1 Destructor
VBase3 Destructor
VBase2 Destructor
VBase1 Destructor
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)