1.5 派生类构造函数、析构函数......
来源:互联网 发布:体育套利软件 编辑:程序博客网 时间:2024/06/15 01:55
1.5 派生类构造函数、析构函数……
派生类的构造函数
每个类都会继承直接基类的所有成员。对于一个最终的派生类来说,它会继承其直接基类的成员,该直接基类的成员又含有其基类的成员,以此类推直至继承链的顶端。因此,最终的派生类将包含它的直接基类的子对象以及每个间接基类的子对象。
每个类控制它自己的成员初始化过程。
尽管在派生类对象中含有从基类继承而来的成员,但是派生类并不能直接初始化这些成员。和其他创建了基类对象的代码一样,派生类也必须使用基类的构造函数来初始化它的基类部分。派生类对象的基类部分与派生类对象自己的数据成员都是在构造函数的初始化阶段执行初始化操作的。类似于我们初始化成员的过程,派生类构造函数同样是通过构造函数初始化列表来将实参传递给基类构造函数的。
除非我们特别指出,否则派生类对象的基类部分会向数据成员一样执行默认初始化。如果想使用其他的基类构造函数,我们需要以类名加圆括号内的实参列表的形式为构造函数提供初始值。这些实际参数将帮助编译器决定到底应该选用哪个构造函数来初始化派生类对象的基类部分。
首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员。
#include <iostream>using namespace std;class Base{public: Base() = default; Base(int m, int n, int k): b1(m), b2(n), b3(k) {} int b1;protected: int b2;private: int b3;};class Derived: public Base{public: Derived() = default; Derived(int a, int b, int c, int d, int e, int f): Base(a, b, c), d1(d), d2(e), d3(f) {} Derived(int a, int b, int c): d1(a), d2(b), d3(c) {} int d1;protected: int d2;private: int d3;};int main(){ Base base1(1,2,3); Derived derived1(4,5,6,7,8,9); Derived derived2(10,11,12); cout << base1.b1 << endl; //1 cout << derived1.b1 << " " << derived1.d1 << endl; //4 7 cout << derived2.b1 << " " << derived2.d1 << endl; //未定义 10 return 0;}
继承的构造函数
在C++11新标准中,派生类能够重用其直接基类定义的构造函数。尽管如我们所知,这些构造函数并非以常规的方式继承而来,但是为了方便,我们不妨姑且称其为“继承”的。
一个类只初始化它的直接基类,出于同样的原因,一个类也只继承其直接基类的构造函数。
类不能继承默认构造函数、拷贝构造函数、移动构造函数,如果派生类没有直接定义这些构造函数,则编译器将为派生类合成它们。
派生类继承基类构造函数的方式是提供一条注明了(直接)基类名的using声明语句。
通常情况下,using声明语句只是令某个名字在当前作用域内可见。而当作用于构造函数时,using声明语句将令编译器产生代码。
对于基类的每个构造函数,编译器都生成一个与之对应的派生类构造函数。换句话说,对于基类的每个构造函数,编译器都在派生类中生成一个形参列表完全相同的构造函数。
这些编译器生成的构造函数如:
derived(parms) : base(args){}
其中,derived是派生类的名字,base是基类的名字,parms是构造函数的形参列表,args将派生类构造函数的形参传递给基类的构造函数。
如果派生类含有自己的数据成员,则这些成员将被默认初始化。
#include <iostream>using namespace std;class Base{public: Base() = default; Base(int m, int n, int k): b1(m), b2(n), b3(k) {} int b1;protected: int b2;private: int b3;};class Derived: public Base{public: using Base::Base; // 等价于Drived(int a,int b,int c): Base(a,b,c) {} int d1;protected: int d2;private: int d3;};int main(){ Derived derived1(4,5,6); cout << derived1.b1 << " " << derived1.d1 << endl;//4 未定义 Derived derived; //合成默认构造函数 Derived derived2 = derived1; //合成拷贝构造函数 return 0;}
派生类的析构函数
和构造函数以及赋值运算符不同的是,派生类析构函数只负责销毁由派生类自己分配的资源。
对象销毁的顺序正好与其创建的顺序相反:派生类析构函数首先执行,然后是基类的析构函数,以此类推,沿着继承体系的反方向直至最后。
class D: public Base{ public: ~D(){} // Base::~Base()被自动调用执行}
派生类的拷贝构造函数
当为派生类定义拷贝构造函数时,我们通常使用基类的拷贝构造构造函数初始化派生类对象的基类部分。
class D: public Base{ public: //Base(d)一般会匹配Base的拷贝构造函数。 //即,将一个D类型的对象d绑定到Base拷贝构造函数的Base&形参上。 //Base的拷贝构造函数负责将D类型的对象d的基类部分拷贝给要创建的派生类对象的基类部分。 D(const D &d): Base(d), D的成员初始化{派生类拷贝构造函数函数体;}}
在默认情况下,基类默认构造函数初始化派生类对象的基类部分。
如果我们想拷贝基类部分,则必须在派生类的构造函数初始值列表中显式地使用基类的拷贝构造函数。
class D: public Base{ public: //假设我们没有提供基类的初始值, //Base的默认构造函数将被用来初始化派生类对象的基类部分。 D(const D &d): D的成员初始化{派生类拷贝构造函数函数体;}}
派生类的拷贝赋值运算符
与拷贝构造函数一样,派生类的赋值运算符必须显示地为派生类对象的基类部分赋值。
class D: public Base{ public: D& operator=(const D &rhs) { //显式调用基类赋值运算符,令其为派生类对象的基类部分赋值 Base::operator=(rhs); //为派生类对象自定义的成员赋值 }}
综合示例
#include <iostream>using namespace std;class Base{public: int a; int b; int c; Base() = default; Base(int l, int m, int n):a(l), b(m), c(n){} Base(const Base &object): a(object.a), b(object.b), c(object.c){} Base& operator=(const Base &object) { a = object.a; b = object.b; c = object.c; return *this; }};class Derived: public Base{public: int d; int e; int f; Derived() = default; Derived(int h, int i, int j):d(h), e(i), f(j){} Derived(int h, int i, int j, int k, int l, int m):Base(h,i,j), d(k), e(l), f(m){} Derived(const Derived &object): Base(object),d(object.d),e(object.e),f(object.f){} Derived&operator=(const Derived &object) { Base::operator=(object); d = object.d; e = object.e; f = object.f; return *this; }};int main(){ Base base; Base base1(1,2,3); Derived derived; Derived derived1(11,12,13); Derived derived2(21,22,23,24,25,26); Derived derived3(31,32,33,34,35,36); derived2 = derived3; cout << base1.a << base1.b << base1.c << endl; cout << derived2.a << derived2.b << derived2.c << endl; cout << derived2.d << derived2.e << derived2.f << endl;}
- 1.5 派生类构造函数、析构函数......
- 派生类构造与析构函数
- 派生类 构造函数
- 派生类的构造函数和析构函数
- 派生类的构造函数和析构函数
- 派生类的构造函数与析构函数
- 关于派生类的构造函数和析构函数
- 派生类中的构造函数与析构函数
- 派生类的构造函数和析构函数
- 派生类的构造函数和析构函数
- 派生类的构造函数与析构函数
- C++派生类的构造函数和析构函数
- 派生类的构造函数与析构函数
- C++ 派生类的构造函数和析构函数
- [c++]派生类的构造函数和析构函数
- 派生类的构造函数和析构函数
- 派生类的构造函数及析构函数
- C++派生类的构造函数和析构函数
- 第九章 Qt拖放
- NOIP复赛复习(九)如何设计测试数据?
- The JSP specification requires that an attribute name is preceded by whitespace
- C/CPP程序的执行过程
- 国际公认反洗钱师协会(ACAMS)最权威的反洗钱定义、方式、措施、监管机构和政策汇总
- 1.5 派生类构造函数、析构函数......
- 局域网访问本地环境
- 使用HttpURLConnection访问网络接口,展示列表数据
- linux内核源码基础
- sql优化
- 巨杉数据库 王涛:如何打造金融级数据库
- CentOS7编译安装Python3.6.3
- CodeForces
- Result Maps collection already contains value for XXXX