C++继承
来源:互联网 发布:皮皮麻将算法 编辑:程序博客网 时间:2024/06/04 20:00
一、基本概念
1、类的继承,是新的类从已有类那里得到已有的特性。或从已有类产生新类的过程就是类的派生。原有的类称为基类或父类,产生的新类称为派生类或子类。
2、如果不显示给出继承方式,默认为private继承。继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。
3、派生类继承基类中除构造和析构函数以外的所有成员。
4、派生类生成:
吸收基类成员(除构造析构函数以外的所有成员);
改造基类成员(根据继承方式调整基类成员的访问,函数在子类中的覆盖,以及虚函数在子类中的覆盖);
添加新的成员;
5、派生类的声明:
class 派生类名:继承方式 基类名1, 继承方式 基类名2,…,继承方式 基类名n
{
派生类成员声明;
};
6、派生类成员的访问属性
二、访问权限
(1) 公有继承
class A//基类{public://公有成员 void get() { cin >> _a1 >> _a2 >> _a3; } void display() { cout << "_a1" << _a1 << endl; cout << "_a2" << _a2 << endl; cout << "_a3" << _a3 << endl; }public: int _a1;protected://保护成员 int _a2;private://私有成员 int _a3;};class B:public A //公有继承{public: void get_1() { cin >> _b1 >> _b2 >> _b3; } void display() { cout << "_a1" << _a1 << endl;//公有成员在拍派生类可以访问 cout << "_a2" << _a2 << endl;//保护成员在派生类中可以访问 cout << "_a3" << _a3 << endl;//_a3为基类私有的,在派生类中不能访问 cout << "_b1" << _b1 << endl; cout << "_b2" << _b2 << endl; cout << "_b3" << _b3 << endl; }public: int _b1;protected: int _b2;private: int _b3;};int main(){ B c1; c1._a1 = 10;//在类外可以访问基类的公有成员 c1._a2;//在类外不可以访问基类的保护成员 c1._a3;//在类外不可以访问基类的私有成员 c1._b1 = 10;//在类外可以访问派生类的公有成员 c1._b2 = 10;//在类外不可以访问派生类的保护成员 c1._b3; //在类外不可以访问派生类的私有成员}
(2)保护继承
class A{public: void get() { cin >> _a1 >> _a2 >> _a3; } void display() { cout << "_a1" << _a1 << endl; cout << "_a2" << _a2 << endl; cout << "_a3" << _a3 << endl; }public: int _a1;protected: int _a2;private: int _a3;};class B :protected A{public: void get_1() { cin >> _b1 >> _b2 >> _b3; } void display() { cout << "_a1" << _a1 << endl;//公有成员在拍派生类可以访问 cout << "_a2" << _a2 << endl;//保护成员在派生类中可以访问 cout << "_a3" << _a3 << endl;//_a3为基类私有的,在派生类中不能访问 cout << "_b1" << _b1 << endl; cout << "_b2" << _b2 << endl; cout << "_b3" << _b3 << endl; }public: int _b1;protected: int _b2;private: int _b3;};int main(){ B c1; c1._a1 = 10;//在类外不可以访问基类的公有成员 c1._a2;//在类外不可以访问基类的保护成员 c1._a3;//在类外不可以访问基类的私有成员 c1._b1 = 10;//在类外可以访问派生类的公有成员 c1._b2 = 10;//在类外不可以访问派生类的保护成员 c1._b3; //在类外不可以访问派生类的私有成员}
(3)私有继承
class A{public: void get() { cin >> _a1 >> _a2 >> _a3; } void display() { cout << "_a1" << _a1 << endl; cout << "_a2" << _a2 << endl; cout << "_a3" << _a3 << endl; }public: int _a1;protected: int _a2;private: int _a3;};class B :private A{public: void get_1() { cin >> _b1 >> _b2 >> _b3; } void display() { cout << "_a1" << _a1 << endl;//公有成员在拍派生类可以访问 cout << "_a2" << _a2 << endl;//保护成员在派生类中可以访问 cout << "_a3" << _a3 << endl;//_a3为基类私有的,在派生类中不能访问 cout << "_b1" << _b1 << endl; cout << "_b2" << _b2 << endl; cout << "_b3" << _b3 << endl; }public: int _b1;protected: int _b2;private: int _b3;};int main(){ B c1; c1._a1 = 10;//在类外不可以访问基类的公有成员 c1._a2;//在类外不可以访问基类的保护成员 c1._a3;//在类外不可以访问基类的私有成员 c1._b1 = 10;//在类外可以访问派生类的公有成员 c1._b2 = 10;//在类外不可以访问派生类的保护成员 c1._b3; //在类外不可以访问派生类的私有成员}
三、派生类的构造函数和析构函数
1、派生类中由基类继承而来的成员的初始化工作还是由基类的构造函数完成,然后派生类中新增的成员在派生类的构造函数中初始化。
class A{public: A(int a) :_a(a) { cout << "A(int a)" << endl; } A(const A& a) { cout << "A(const A& a)" << endl; } A& operator=(const A& a) { cout << "A& operator=(const A& a)" << endl; } ~A() { cout << "~A()" << endl; }public: int _a;};class B:public A{public: B(int a,int b) :A(a) //初始化基类 ,_b(b) { cout << "B(int b)" << endl; } B(const B& b) :A(b) //切片 , _b(b._b) { /*cout << "B(const B& b)" << endl;*/ } B& operator=(const B& b) { if (this != &b) { A::operator=(b);//会发生同名隐藏 故加作用域 _b = b._b; } return *this; } ~B() { A::~A();//将输出两次~A() }public: int _b;};int main(){ B b1(1, 2); B b2(b1); b1.~B(); getchar(); return 0;}
2、如果基类中没有不带参数的构造函数,那么在派生类的构造函数中必须调用基类构造函数,以初始化基类成员。
3、基类没有定义构造函数,则派生类也可以不用定义,全部使用缺省构造函数
4、基类定义了带有形参列表构造函数,派生类就一定义构造函数
5、派生类构造函数执行的次序:
四、同名隐藏:
在继承体系中,基类和派生类有同名的成员,如果使用派生类对象调用基类和派生类中的同名成员,优先调用派生类的同名成员
1、成员名相同:
class A{public: int a;public: void print1() { cout << "a=" << a << endl; cout << "A:" << endl; }};class B :public A{public: int a;public: void print() { cout << "a=" << a << endl; cout << "B:" << endl; }};int main(){ B c2; c2.a = 0; //c2.A::a = 0; c2.print1(); c2.print(); getchar(); return 0;}
c2.a = 0这样访问的直接是派生类B中的a,如果想访问基类中的a,则需要写成这样 c2.A::a = 0
2、函数名相同:
class A{public: void print() { cout << "A:" << endl; }};class B :public A{public: void print() { cout << "B:" << endl; }};int main(){ B c2; c2.print(); //c2.A::print(); getchar(); return 0;}
五、继承与静态成员:
class A{public: int _a; static int count;};class B :public A{public: int _b;};class C :public B{public: int _c;};int main(){ cout << sizeof(C) << endl;//12 getchar(); return 0;}
类的大小与静态成员无关,而且无论派生多少个子类,都只有一个static成员实例。
六、三种继承:
一个派生类同时有多个基类,这种情况称为多重继承,
派生类只有一个基类,称为单继承。
单继承:
class A{public: int _a;};class B :private A{public: int _b;};int main(){ cout << sizeof(B) << endl;//8 getchar(); return 0;}
多继承:
class A{public: int _a;};class B {public: int _b;};class C:public A,public B{public: int _c;};int main(){ cout << sizeof(C) << endl;//12 getchar(); return 0;}
菱形继承:
class A{public: int _a;};class B : public A{public: int _b;};class C:public A{public: int _c;};class D:public B,public C{public: int _d;};int main(){ cout << sizeof(D) << endl;//20 getchar(); return 0;}
七、菱形继承存在的二义性和数据冗余:(虚拟继承)
上面的菱形继承代码在类外访问时可能存在问题,如主函数给成这样:
int main(){ cout << sizeof(D) << endl; D d; d._a = 1; d._b = 2; d._c = 3; d._d = 4; getchar(); return 0;}
这样派生类D中就会有两份基类A的成员,而通过派生类赋值时由于有两份_a,这时编译器就会报错
这就是菱形继承里的二义性
要解决二义性可以加上类名
如:
int main(){ D d; d.C::_a = 1; d.B::_a = 1; return 0;}
这样做虽然解决了二义性问题,但数据冗余问题仍然存在,如果集成体系中数据繁多,将会造成大量的数据冗余,占用内存;想要解决数据冗余问题,这时就需要用到虚继承,给继承前面加上关键字virtual就解决了
即虚拟继承:
class A{public: int _a;};class B : virtual public A{public: int _b;};class C:virtual public A{public: int _c;};class D:public B,public C{public: int _d;};int main(){ D d; d._a = 1; d._b = 2; d._c = 3; d._d = 4; cout << sizeof(D) << endl;//24 getchar(); return 0;}
虚继承既然解决了数据冗余,为什么字节数还反倒增加了?这时就需要调用内存来分析
- c继承
- C++----------------继承
- 【c#】继承
- C++:继承
- C++::继承
- [C++]继承
- 【C++】继承
- 【C#】继承
- 【c++】继承
- 【C++】继承
- c#-继承
- 【C++】 继承
- 【C#】继承
- C++|继承
- 【C++】继承
- C/C++--私有继承
- [C/C++]继承
- c++:私有继承,公有继承,保护继承
- ActionContext和ServletActionContext
- 自然语言识别(1)--利用bosonNLP分析歌词感情
- 201710020117->unity中monosingleton
- Olympic Parade UVALive
- Python中解决Gensim找不到模块的问题
- C++继承
- scel2txt 搜狗scel格式转txt python3
- Git 下载安装 配置
- hdu5091 线段树扫描线
- oneinstack一键包Nginx php多版本共存配置全过程
- 使用mybatis的延迟加载
- 有关QT mingGW 5.4.2中的一些问题记录
- poj3088:Snowflake (Hash)
- Python 简单的模拟wireshark抓包工具