学习内容
本节学习 继承构造 在各版本的异同~,后续请关注 学习C++11/14/17/20/23关键词版本更替 ,将持续更新~~~~
using 声明
可将定义在其他文件的名字引入到using声明出现的区域内,using声明可用于将命名空间成员引入其他命名空间和块作用域,或将基类成员引入派生类定义中,或将枚举器引入命名空间、块和类作用域
cpp
namespace Shape
{
void GetCurShape() { cout << "rect shape" << endl; };
}
void main()
{
using Shape::GetCurShape;
GetCurShape(); //直接调用,不需要写Shape::GetCurShape();
}
/////////////////////////////////
class Base
{
protected:
int value = 10;
}
class Derived: public Base
{
public:
using Base::value; //使用基类的value
void fun() { cout << " value : " << value << endl; }
}
//////////////////////////////////////////
//c++20新增 using枚举器
enum class Color { Red , Green , Blue };
void main()
{
using enum Color;
Color c = Red; // 不需要在想域 Color::Red
}
在类定义中: using声明将基类的成员(变量或函数)引入到派生类中。如果被引入的成员是函数,那么父类存在所有相同名称的函数都被引入。如果派生类存在相同的名称、参数列表和限定符(cv限定)的成员,则派生类成员会隐藏或覆盖从基类引入的成员
cpp
class Base
{
public:
void print() { cout << " this is a print func " <<endl ; }
void show() { cout << " this is a show func " << endl; }
void setStep( char c ) { cout << " this is a setStep func " << endl; }
protected: //使用保护权限,确保派生类可以看见
int m ;
typedef int valueInt;
}
class Derived : public Base
{
public:
using Base::m; //m在Derived范围时公开权限
using Base::valueInt; //valueInt在Derived范围时公开圈选
using Base::print;
void print() override { cout << "this is a ovrride func " << endl; } //Derived的print函数重写了父类函数
using Base::show;
void show() { cout << " this is a Dervied func " <<endl; } //派生类show函数直接覆盖了父类的函数
using Base::setStep;
void setStep( int) { cout << " this is a Dervied func " << endl; } //派生类的setStep和父类的setStep并存
}
继承构造
如果使用的using引入的是直接基类的构造函数,即using Base::Base,则Base内部所有的构造函数都对初始化派生类的重载决议可见(基类的构造函数可作为派生类的构造函数)
当进行初始化派生类时,如果选用的是基类的继承构造,那么就是用基类的构造函数进行初始化,其他未直接使用继承构造的,可使用默认的构造函数。如果不存在默认构造函数,则进行默认初始化
cpp
class Base
{
Base(int) {}
Base(string) {}
}
int GetValue () { return 19; }
class Derived: public Base
{
public:
using Base::Base; //继承构造
int data;
int y = GetValue();
}
void test()
{
Dervied d ("Hello"); //Ok 使用基类的Base(string)
Derived d1; //error,不存在默认的构造函数
}
cpp
class A { A (int ) ; };
class B : virtual A
{
public:
using A::A;
B() = delete;
}
class C : public B
{
public:
using B::B;
}
class D : public C , virtual A
{
public:
using C::C;
}
D d(10); //编译通过: 直接使用虚基类的A的构造函数
无论虚基类被多少层派生类继承,其子对象始终由最派生对象负责初始化,中间派生类对虚基类的初始化都会被编译器忽略
(最派生对象:运行时实际创建的对象)
虚基类:为了解决菱形继承导致的二义性产生
与其他非静态成员函数using声明一样,如果继承的构造函数与Dervied的某个构造函数的签名匹配(参数列表相同),则它会被Derived中的版本隐藏。
如果父类的某个继承构造正好与子类的拷贝/移动构造函数相匹配,则不会阻止Dervied拷贝/移动构造函数的隐式生成,同样隐藏继承的版本(出现多重继承使用虚基类,避免病态对象),同样适用于拷贝/移动赋值运算符
在一个模板类中,如果一个using引入了一个依赖名,如果引入符号左右两边名称相同(::),则默认命名一个构造函数
cpp
template <class T>
class A : T
{
using T::T; //构造函数
}
注意
只有using声明的明确提及名称才会被转移到声明性作用域;当枚举类型名称被using声明时,枚举器不会转移
using声明不能引用命名空间、有作用域的枚举器(C++20之前)、基类的析构或用于自定义转换函数的成员模板特化
using声明不能命名成员模板特化
cpp
class A
{
public:
template < class T>
void f(){};
}
class B : public A
{
public:
using A::f(); //ok
using A::f<int>(); //error ,不允许引入模版特化
}
using声明符不能用于将依赖成员模板的名称作为引入,也不允许对依赖名用template来消除歧义
cpp
template< class X >
class B
{
public:
//称为依赖模板函数,fun函数既一来X ,又依赖T
template <class T>
void fun(T){}
}
template <class Y >
class D : public B<Y>
{
public:
using B<Y>::f;
using B<Y>::template f; //error ,不允许对依赖名进行template指定
void test()
{
f(1); //ok 引入的是f作为普通成员函数的使用,并未引入模板f
f<int>(2); //error ,f只是一个普通成员函数
}
}
using声明中的包扩展(C++17引入新概念)使得无需递归即可形成一个暴露可变基类重载成员类
cpp
//万能重载器
template <typename... Ts> //可变模板参数
class Overloader : Ts...
{
public:
using Ts::operator()...; //将()运算符暴露给每个参数,供每个调用者使用
}
template <typename... T>
Overloader(T...) ->Overloader<T...>; //类模板指引推导,当使用Overloader{...} 时,自动把传入的参数推导为模板参数T...
auto o = Overloader{
[](int a){ cout << "this is a integral value " << endl; },
[](string s){ cout << " this is a string value " << endl; }
};
C++11存在的旧版继承构造函数具体规则和行为
当在派生类中使用基类的构造函数时,会发生以下行为:
1、收集基类中所有存在的构造函数(包含带默认参数和模板构造参数)
2、生成候选继承构造函数
cpp
class Base
{
protected:
Base(int a = 11 , int b = 21){}
}
class Dervied : public Base
{
public:
using Base::Base;
//默认生成以下
/*
Base(int ,int )
Base(int)
Base()
*/
}
3、在派生类中隐式声明新的构造函数:新的构造函数会把参数直接转发给对应基类的构造函数,并和基类构造函数访问权限已知
4、派生类自己用户定义构造函数会优先于继承的构造函数;
C++17(代码参考C++11做出变动)
解决C++11中存在的代码冗余、重载决议歧义、ABI兼容性差、虚基类处理混乱等问题
1、取消冗余重载生成:不再为基类带默认参数的构造生成多个版本,保留原始的签名
2、转发语义代替生成:继承的构造函数不再是派生类生成新的构造,而是直接转发基类构造
3、继承构造函数不再干扰模板重载决议
4、提升ABI兼容性:主流编译器均稳定实现,跨平台一致性
5、明确优先级:派生类自定义构造>继承构造函数
C++20, 功能增强
主要改进点集中在constexpr支持、多继承/虚基类优化、模板构造继承
1、constexpr继承构造自动推导:若基类的构造函数是constexpr,那么派生类继承后仍然是constexpr
2、多继承+同名构造无歧义:多个基类有同名构造时,可通过作用域显式指定继承
3、虚基类构造函数规则明确:虚基类构造函数可被继承
4、模版基类构造继承优化:模板基类的成员模板构造,继承后可正常参与模板参数推导
cpp
template <typename T>
class Base
{
public:
template <class U>
Base( U && val) : value (static_cast<T>(val)) {}
T value;
}
template <class T>
class Derived:public Base
{
public:
using Base<T>::Base;
}
Derived<int> d(3.14); //C++17时,不存在子类模板参数类型直接推导到基类模板参数,3.14并未正确推导类型到Base内部的U,C++20已解决这个问题
C++23 优化细节
1、模版基类实例化时的继承构造延迟解析:模板派生类继承模板基类的构造时,延迟到模板实例化阶段解析构造签名
2、私有继承的构造函数访问优化:私有继承基类时,继承的构造函数可通过using显示提升访问权限
3、委托构造与继承构造的兼容:派生类的委托构造可正常调用继承的构造函数
4、空基类优化与继承构造的兼容:继承空基类的构造函数时,可触发空基类优化,不增加派生类的内存体积
cpp
template <class T>
class Base
{
public:
T value;
Base(T v) : value (v) {}
Base() :value( T{} ) {}
}
template <class T>
class Derived : public Base<T> //C++20当进行到该处时,已经提前开始解析Base<T>,此时T并未确定,会触发一个编译警告
{
public:
using Base<T>::Base; //延迟实例化解析
T getValue () { return value; }
}
Derived<int> d(
100); //只有当d被实例化时,才解析对应基类实例的构造函数签名 C++23