继承的构造函数
-
一个类只继承其直接基类的构造函数,默认/拷贝/移动构造函数是不能被继承的;
-
继承Father的构造函数
using Father::Father; //继承Father的构造函数 using让某个名字在当前的作用域可见; //编译器遇到这条代码的时候,会把基类的每个构造函数都生成一个与之对应的派生类构造函数
-
如果基类A的构造函数有默认参数的话,那么编译器遇到这种using A::A的时候,就会帮咱们在派生类B中构造出多个构造函数来;
- 第一个构造函数时带有所有参数的构造函数;
- 其余的构造函数,每个分别省略掉一个默认参数;
//示例: Son(int i, int j, int k = 5) :Father(i, j, k){}; Son(int i, int j, int k):Father(i,j,k){}; Son(int i, int j):Father(i,j){};
-
如果基类含多个构造函数,则多数情况下,派生类会继承所有这些构造函数,但如下情况例外:
- 如果在派生类中定义的构造函数与基类构造函数有相同的参数列表,那么从基类中继承来的构造函数会被你在派生类中定义的覆盖掉,相当于只继承了一部分构造函数;
- 默认/拷贝/移动构造函数是不会被继承的;
- 如果子类,只含有using Father::Father从Father继承来的构造函数的话,那么编译器会给它合成默认的构造函数;
#include <iostream>
using namespace std;
class Father
{
public:
Father(int i, int j, int k)
{
}
};
class Son : public Father
{
public:
using Father::Father; //继承Father的构造函数 using让某个名字在当前的作用域可见;
//编译器遇到这条代码的时候,会把基类的每个构造函数都生成一个与之对应的派生类构造函数
};
int main()
{
Son son(3, 4, 5);
}
多重继承
从多个父类产生出子类,多重继承;
多重继承概述
#include <iostream>
using namespace std;
class Base
{
public:
Base(int i) :m_value_base(i) {};
public:
int m_value_base;
virtual ~Base()
{
}
void my_info()
{
cout << m_value_base << endl;
}
};
class Father_A: public Base
{
public:
Father_A(int i) :Base(i), m_value_a(i) //每个子类的构造函数,负责解决自己父类的初始化问题
{
};
virtual ~Father_A()
{
}
void my_info()
{
cout << m_value_a << endl;
}
public:
int m_value_a;
};
class Father_B
{
public:
Father_B(int i):m_value_b(i) //每个子类的构造函数,负责解决自己父类的初始化问题
{
};
virtual ~Father_B()
{
}
void my_info()
{
cout << m_value_b << endl;
}
public:
int m_value_b;
};
//class默认是私有继承
//struct默认是共有继承
class Son :public Father_A, public Father_B
{
public:
Son(int i, int j, int k) :Father_A(i), Father_B(j), m_value_c(k)
{
}
virtual ~Son()
{
}
void my_info_c()
{
cout << m_value_c << endl;
Father_A::my_info(); //调用父类Father_A的同名函数
Father_B::my_info(); //调用父类Father_B的同名函数
}
public:
int m_value_c;
};
int main()
{
Son son(10, 20, 50);
//多重继承的二义性问题
//1. 通过作用域来解决
son.Father_A::my_info(); //增加作用域,明确告诉系统调用的是父类A还是父类B的成员函数
//派生类会包含每个基类的子对象
}
静态成员变量
静态成员属于类,不属于对象;
为了能够使用,需要定义静态成员变量;如果代码中不用该静态变量,可以不定义;如果用到该静态变量,可以定义;否则,链接出错;
#include <iostream>
using namespace std;
class Base
{
public:
Base(int i) :m_value_base(i) {};
public:
virtual ~Base()
{
}
void my_info()
{
cout << m_value_base << endl;
}
public:
int m_value_base;
static int m_static;
};
int Base::m_static = 5;
class Father_A: public Base
{
public:
Father_A(int i) :Base(i), m_value_a(i) //每个子类的构造函数,负责解决自己父类的初始化问题
{
};
virtual ~Father_A()
{
}
void my_info()
{
cout << m_value_a << endl;
}
public:
int m_value_a;
};
class Father_B
{
public:
Father_B(int i):m_value_b(i) //每个子类的构造函数,负责解决自己父类的初始化问题
{
};
virtual ~Father_B()
{
}
void my_info()
{
cout << m_value_b << endl;
}
public:
int m_value_b;
};
//class默认是私有继承
//struct默认是共有继承
class Son :public Father_A, public Father_B
{
public:
Son(int i, int j, int k) :Father_A(i), Father_B(j), m_value_c(k)
{
}
virtual ~Son()
{
}
void my_info_c()
{
cout << m_value_c << endl;
Father_A::my_info(); //调用父类Father_A的同名函数
Father_B::my_info(); //调用父类Father_B的同名函数
}
public:
int m_value_c;
};
int main()
{
//派生类会包含每个基类的子对象
Base::m_static = 1;
Father_A::m_static = 2;
Son::m_static = 3;
Son son(10, 20, 30);
son.m_static = 15;
}
派生类构造函数和析构函数
- 构造一个派生类对象将同时构造并初始化所有基类子对象;
- 派生类的构造函数初始化列表只初始化它的直接基类,每个类的构造函数都负责初始化它的直接基类,就会让所有类都初始化;
- 派生类构造函数初始化列表将实参分别传递给每个直接基类;基类的构造顺序跟派生列表(继承的顺序 )中基类的出现顺序保持一致;
- 析构函数的调用顺序与构造函数的顺序完全相反;
显示的初始化基类和隐式的初始化基类
- 隐式的初始化基类: 派生类的构造函数初始化列表中没有调用带参数的基类构造函数,而是调用的基类的默认构造函数来实现基类的初始化;
#include <iostream>
using namespace std;
class Base
{
public:
Base(int i) :m_value_base(i)
{
cout << "Base构造函数执行" << endl;
};
public:
virtual ~Base()
{
cout << "Base析构函数执行" << endl;
}
public:
int m_value_base;
};
class Father_A: public Base
{
public:
Father_A(int i) :Base(i), m_value_a(i) //每个子类的构造函数,负责解决自己父类的初始化问题
{
cout << "Father_A构造函数执行" << endl;
};
virtual ~Father_A()
{
cout << "Father_A析构函数执行" << endl;
}
public:
int m_value_a;
};
class Father_B
{
public:
Father_B(int i):m_value_b(i) //每个子类的构造函数,负责解决自己父类的初始化问题
{
cout << "Father_B构造函数执行" << endl;
};
virtual ~Father_B()
{
cout << "Father_B析构函数执行" << endl;
}
public:
int m_value_b;
};
//class默认是私有继承
//struct默认是共有继承
class Son :public Father_A, public Father_B
{
public:
Son(int i, int j, int k) :Father_A(i), Father_B(j), m_value_c(k)
{
cout << "Son构造函数执行" << endl;
}
virtual ~Son()
{
cout << "Son析构函数执行" << endl;
}
public:
int m_value_c;
};
int main()
{
Son son(10, 20, 30);
}
从多个父类继承构造函数
如果一个类从它的基类中继承了相同的构造函数,这个类必须为该构造函数定义它的自己的版本;
子类要定义同参数的构造函数的自己的版本;
#include <iostream>
using namespace std;
class Father_A
{
public:
Father_A(int tv) {};
};
class Father_B
{
public:
Father_B(int tv) {};
};
class Son :public Father_A, public Father_B
{
using Father_A::Father_A;//继承Father_A的构造函数 Son(int tv):Father_A(tv){}
using Father_B::Father_B;//继承Father_B的构造函数 Son(int tv):Father_B(tv){}
Son(int tv) :Father_A(tv), Father_B(tv) {};
};
int main()
{
}
类型转换
基类指针可以指向一个派生类对象:编译器隐式执行这种派生类到基类的转换,转换成功的原因是每个派生类对象都包含一个基类对象部分;所以基类的引用或者指针是可以绑定到基类对象这部分;
上述对于多重继承同样成立;
#include <iostream>
using namespace std;
class Base
{
public:
Base(int i) :m_value_base(i)
{
cout << "Base构造函数执行" << endl;
};
public:
virtual ~Base()
{
cout << "Base析构函数执行" << endl;
}
public:
int m_value_base;
};
class Father_A: public Base
{
public:
Father_A(int i) :Base(i), m_value_a(i) //每个子类的构造函数,负责解决自己父类的初始化问题
{
cout << "Father_A构造函数执行" << endl;
};
virtual ~Father_A()
{
cout << "Father_A析构函数执行" << endl;
}
public:
int m_value_a;
};
class Father_B
{
public:
Father_B(int i):m_value_b(i) //每个子类的构造函数,负责解决自己父类的初始化问题
{
cout << "Father_B构造函数执行" << endl;
};
virtual ~Father_B()
{
cout << "Father_B析构函数执行" << endl;
}
public:
int m_value_b;
};
//class默认是私有继承
//struct默认是共有继承
class Son :public Father_A, public Father_B
{
public:
Son(int i, int j, int k) :Father_A(i), Father_B(j), m_value_c(k)
{
cout << "Son构造函数执行" << endl;
}
virtual ~Son()
{
cout << "Son析构函数执行" << endl;
}
public:
int m_value_c;
};
int main()
{
Base* base = new Son(1, 2, 3);
Father_A* fa = new Son(1, 2, 3);
Father_B* fb = new Son(1, 2, 3);
Son son(10 ,20, 30);
Base b(son);
}
虚基类和虚继承
派生列表中,同一个基类只能出现一次,但是如下两种情况例外;
-
派生类可以通过它的两个直接基类分别继承同一个间接基类;
-
直接继承某个基类,然后通过另一个基类间接继承
虚基类和虚继承
- 无论这个类在继承体系中出现多少次,派生类中,都只会包含唯一一个共享的虚基类字内容;不会出现访问不明确的问题;
- 虚继承只是对孙子类有意义,对孙子类的直接基类没有意义;例Son继承Father_A和Father_C,Father_A和Father_C继承Base;那么Father_A和Father_B虚继承Base,对Son有意义,避免它间接继承Base两次;
- 虚继承:表示后续从本类派生的类应该共享虚基类Base的同一份实例;
- 虚继承之后:虚基类的初始化使用孙子类;
说明
- 现在是Son类初始化虚基类,如果Son有了子类,那么由Son的子类初始化虚基类;虚基类是由最底层的派生类来初始化;
- 初始化顺序:先初始化虚基类,然后按照派生列表中出现的顺序来初始化其他类;多个虚基类的话,按照派生列表中直接基类往回追溯,看是否这些直接基类中是否含有虚基类,反正是先追溯到哪个,先构造那个虚基类;析构顺序和构造顺序相反;
#include <iostream>
using namespace std;
class Base
{
public:
Base(){}
Base(int i) :m_value_base(i)
{
cout << "Base构造函数执行" << endl;
};
public:
virtual ~Base()
{
cout << "Base析构函数执行" << endl;
}
public:
int m_value_base;
};
class Father_A: virtual public Base //virtual可以和public互换位置
{
public:
Father_A(int i) :Base(i), m_value_a(i) //每个子类的构造函数,负责解决自己父类的初始化问题
{
cout << "Father_A构造函数执行" << endl;
};
virtual ~Father_A()
{
cout << "Father_A析构函数执行" << endl;
}
public:
int m_value_a;
};
class Father_C : virtual public Base
{
public:
Father_C(int i) :Base(i), m_value_c(i) //每个子类的构造函数,负责解决自己父类的初始化问题
{
cout << "Father_C构造函数执行" << endl;
};
virtual ~Father_C()
{
cout << "Father_C析构函数执行" << endl;
}
public:
int m_value_c;
};
class Father_B
{
public:
Father_B(int i):m_value_b(i) //每个子类的构造函数,负责解决自己父类的初始化问题
{
cout << "Father_B构造函数执行" << endl;
};
virtual ~Father_B()
{
cout << "Father_B析构函数执行" << endl;
}
public:
int m_value_b;
};
//class默认是私有继承
//struct默认是共有继承
class Son :public Father_A, public Father_B, public Father_C
{
public:
//虚基类的时候,孙子初始化爷爷
Son(int i, int j, int k) :Base(i), Father_A(i), Father_B(j), Father_C(k), m_value_c(k)
{
cout << "Son构造函数执行" << endl;
}
virtual ~Son()
{
cout << "Son析构函数执行" << endl;
}
public:
int m_value_c;
};
int main()
{
Son son(10 ,20, 30);
//son间接继承了Base两次,导致Base构造了两次;
//son.m_value_base = 10; 两个m_value_base, 二义性
}
总结
- 不太提倡使用多重继承;简单,不容易出现二义性,使用多重继承;
- 使用单一继承能解决的尽量不要用多重继承;
虚继承底层实现
每个虚继承的子类都有一个虚基类指针和虚基类表,虚基类表中存储了 vbptr 在本类的偏移地址,和 vbptr 到虚基类成员之间的偏移,而且虚基类指针是被继承的,这样子类只会保存间接基类的一份拷贝,一般在本类对象内存布局的最后面。
原文链接: https://www.cnblogs.com/NaughtyCoder/p/13359535.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;
也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/369574
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!