16_继承
继承
派生类的定义
class 子类{};
class 子类:继承方式 父类
{
//新增子类数据
};
继承方式: private protected public(推荐)
protected关键字: 基类的保护成员只在派生类的成员函数中能访问
所有父类私有在子类中不可访问, 公共继承保持不变, 保护继承变保护, 私有继承变私有
class Base
{
private:
int a;
protected:
int b;
public:
int c;
};
class Son:public Base
{
//子类中 能访问 Protected b public c
public:
void func()
{
cout << b << c << endl;
cout << a << endl; //不可访问
}
};
void main()
{
Son ob;
cout << ob.b << endl; //类外无法访问
}
继承中的构造和析构
子类的构造析构顺序
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "父类构造" << endl;
}
~Base()
{
cout << "父类析构" << endl;
}
};
class Other
{
public:
Other()
{
cout << "Other构造" << endl;
}
~Other()
{
cout << "Other析构" << endl;
}
};
class Son: public Base
{
public:
Other ob;
public:
Son()
{
cout << "Son构造" << endl;
}
~Son()
{
cout << "Son析构" << endl;
}
};
int main()
{
Son ob;
return 0;
}
子类调用成员对象, 父类的有参构造
子类实例化对象时, 会自动调用成员对象, 父类的默认无参构造
子类实例化对象时必须使用初始化列表 调用成员对象, 父类的有参构造
初始化列表时: 父类写类名称 成员对象用对象名
#include <iostream>
using namespace std;
class Base
{
public:
int a;
public:
Base()
{
cout << "父类默认构造" << endl;
}
Base(int a)
{
this->a = a;
cout << "父类有参构造" << endl;
}
~Base()
{
cout << "父类析构" << endl;
}
};
class Other
{
public:
int b;
public:
Other()
{
cout << "Other默认构造" << endl;
}
Other(int b)
{
this->b = b;
cout << "Other有参构造" << endl;
}
~Other()
{
cout << "Other析构" << endl;
}
};
class Son: public Base
{
public:
Other ob;
int c;
public:
Son()
{
cout << "Son默认构造" << endl;
}
Son(int a, int b, int c): Base(a), ob(b)
{
this->c = c;
cout << "Son有参构造" << endl;
}
~Son()
{
cout << "Son析构" << endl;
}
};
int main()
{
Son ob(1, 2, 3);
return 0;
}
子类和父类的同名处理
同名成员 最简单 最安全的处理方式: 加作用域
子类和父类 同名成员数据
子类默认优先访问 子类的同名成员
必须加父类作用域 访问父类的同名成员
#include <iostream>
using namespace std;
class Base
{
public:
int a;
public:
Base(int a)
{
this->a = a;
}
void fun()
{
cout << "Base的fun()" << endl;
}
};
class Son: public Base
{
public:
int a;
Son(int x, int y): Base(x)
{
a = y;
}
void fun()
{
cout << "Son的fun()" << endl;
}
};
int main()
{
Son ob(10, 20);
//同名成员 子类默认优先访问
cout << ob.a << endl; //20
//加父类作用域访问父类同名成员
cout << ob.Base::a << endl; //10
ob.fun();
ob.Base::fun();
return 0;
}
子类 重定义 父类的同名函数
重载: 无继承, 同一作用域, 参数的个数, 顺序, 类型 不同 都可重载
重定义: 有继承, 子类 重定义 父类的同名函数(参数可以不同) 子类一旦重定义父类的同名函数不管参数是否一致, 子类会屏蔽父类所有的同名函数
#include <iostream>
using namespace std;
class Base
{
public:
void fun()
{
cout << "Base的fun()" << endl;
}
void fun(int a)
{
cout << "Base的fun(int a)" << endl;
}
void fun(int a, int b)
{
cout << "Base的fun(int a, int b)" << endl;
}
};
class Son: public Base
{
public:
void fun(string a)
{
cout << "Son的fun(string a)" << endl;
}
};
int main()
{
Son ob;
ob.fun("hello");
ob.fun(); //error
ob.fun(5); //error
ob.fun(10, 20); //error
return 0;
}
子类不能继承父类的成员
不是所有的函数都能自动从基类继承到派生类中。
父类的构造函数和析构函数用来处理对象的创建和析构操作,父类的构造和析构函数只知道对自己的对象做什么,也就是说构造函数和析构函数不能被继承,必须为每一个特定的派生类分别创建。
另外operator=也不能被继承,因为它完成类似构造函数的行为。也就是说尽管我们知道如何由=右边的对象如何初始化=左边的对象的所有成员,但是这个并不意味着对其派生类依然有效。在继承的过程中,如果没有创建这些函数,编译器会自动生成它们。
多继承
多继承的格式
class 父类1{};
class 父类2{};
class 子类:继承方式1 父类1, 继承方式2 父类2
{
//新增子类数据
};
#include <iostream>
using namespace std;
class Base1
{
public:
int a;
Base1(int a)
{
this->a = a;
}
};
class Base2
{
public:
int b;
Base2(int b)
{
this->b = b;
}
};
class Son: public Base1, public Base2
{
public:
Son(int a, int b): Base1(a), Base2(b)
{
}
void fun()
{
cout << a << ", " << b << endl;
}
};
int main()
{
Son ob(1, 2);
ob.fun();
return 0;
}
菱形继承
菱形继承: 有公共祖先的继承
最底层的子类数据会包含多份公共祖先数据
class Animal
{
public:
int data;
};
class Sheep :public Animal{};
class Tuo: public Animal{};
class SheepTuo: public Sheep, public Tuo{};
void main()
{
SheepTuo ob;
memset(&ob, 0, sizeof(SheepTuo));
cout << ob.data << endl; //error, 有二义性
cout << ob.Sheep::data << endl;
cout << ob.Tuo::data << endl;
}
解决方法: 加作用域
虚继承
虚继承解决菱形继承中多份公共祖先数据的问题
虚继承的方式
在继承方式前加 virtual 修饰, 子类虚继承父类 子类只会保存一份公共数据
虚继承会在子类中产生虚基类指针(vbptr) 指向虚基类表(vbtable),虚基类表纪录的是通过该指针访问公共祖先的数据的偏移量。
注意:虚继承只能解决具备公共祖先的多继承所带来的二义性问题,不能解决没有公共祖先的多继承的
工程开发中真正意义上的多继承是几乎不被使用,因为多重继承带来的代码复杂性远多于其带来的便利,多重继承对代码维护性上的影响是灾难性的,在设计方法上,任何多继承都可以用单继承代替。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析