Inherited constructors(继承构造)各版本异同

学习内容

本节学习 继承构造 在各版本的异同~,后续请关注 学习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

相关推荐
寻寻觅觅☆7 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
fpcc8 小时前
并行编程实战——CUDA编程的Parallel Task类型
c++·cuda
m0_607076608 小时前
CSS3 转换,快手前端面试经验,隔壁都馋哭了
前端·面试·css3
l1t8 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
赶路人儿8 小时前
Jsoniter(java版本)使用介绍
java·开发语言
NEXT069 小时前
二叉搜索树(BST)
前端·数据结构·面试
NEXT069 小时前
JavaScript进阶:深度剖析函数柯里化及其在面试中的底层逻辑
前端·javascript·面试
ceclar1239 小时前
C++使用format
开发语言·c++·算法
码说AI9 小时前
python快速绘制走势图对比曲线
开发语言·python
Gofarlic_OMS9 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化