C++11之继承构造函数(using 声明)

简介: C++11之继承构造函数(using 声明)

场景

首先我们来看下面这段代码,有一个Class A、还有一个继承于A的Class B,Class B仅仅增加了一个虚函数,但是就需要把Class A的所有构造方法进行“补齐”。

class A
{
public:
  A(int i){}
  A(double d, int i){}
  A(float f, int i, const char* c){}
};
class B : public A
{
public:
  B(int i):A(i) {}
  B(double d, int i):A(d, i) {}
  B(float f, int i, const char* c):A(f, i, c) {}
  virtual void test(){}
};

继承类的创建释放顺序

就以上面A为父类,B为子类为例,当创建一个B类的对象时,先调用父类的构造函数,然后调用自身的构造函数,在进行释放时,先调用自身的析构函数,然后在调用父类的析构函数。(注: 如果有读者不太了解可以在子类和父类中的构造、析构函数中进行日志输出观察。这块不是重点就一笔带过。)


通过using关键字继承

那么对于上述这个例子中,如果子类B在构造函数中不调用父类A对应的构造函数的话,编译器就会自动调用父类A的默认构造函数,结果A没有默认构造函数就会报错。


所以为了解决父类中有好多的构造函数,而导致出现这种繁琐的步骤,在C++11中就引入了,子类可以通过using关键字,通过声明继承父类构造函数。

继承成员函数

这里定义了一个父类Base,并有一个参数为double的f函数。又定义了一个子类Derived继承于Base,其中Derived类中有一个参数为int的f函数,并且在Derived中使用了using Base::f; 表示在Derived中也可以使用父类的f函数。

class Base
{
public:
  void f(double i)
  {
    cout << "Base:" << i << endl;
  }
};
class Derived : public Base  // 默认为public继承
{
public:
  using Base::f;
  void f(int i)
  {
    cout << "Derived:" << i << endl;
  }
};
int main()
{
  B(1);
  Base b;
  b.f(4.6);
  Derived d;
  d.f(4.9);
  d.f(4);
  return 0;
}

运行结果:

Base:4.6
Base:4.9
Derived:4


这里我们通过using声明,就可以看到子类Derived也可以使用父类的f函数。

继承构造函数

这里对文中最开始的那个代码进行一下改造。

class A
{
public:
  A(int i){}
  A(double d, int i){}
  A(float f, int i, const char* c){}
};
class B : public A
{
public:
  using A::A; // 继承构造函数
  virtual void test() {};
};


在子类B中只需要添加一句using A::A;,就可以继承构造函数。

带有默认参数的构造函数

在父类带有默认参数的构造函数,对于继承构造函数来说,参数的默认值是不会被继承的。但是,默认值会导致父类产生较多个构造函数的版本,并且所有的构造函数均会被子类继承。

class A
{
public:
  A(int a = 3, double = 2.4){}
};
class B : public A
{
public:
  using A::A;
};

以上面这段代码为例,就A(int a = 3, double = 2.4)一个构造函数能产生多少个构造函数版本呢??

A类中会有这些:


1.A(int =3, double =2.4) 这就是俩个参数的版本

2.A(int =3)这是只有一个参数的版本

3.A(const A&)这是拷贝构造函数版本

4.A() 这是无参构造函数版本


B类中会有这些:

  1. B(int, double) 这就是俩个参数的版本
  2. B(int =3)这是只有一个参数的版本
  3. B(const A&)这是拷贝构造函数版本 (这不是继承的)
  4. B()无参构造函数


所以在父类中有默认构造函数时,需要特别小心多个版本的构造函数的产生。

继承构造函数导致的“冲突”

这种冲突一般在多继承情况下才会存在 ,由于父类中有相同的构造函数导致的子类中有重复定义类型相同的继承构造函数。

class A
{
public:
  A(int a){}
};
class B
{
public:
  B(int b) {}
};
class C : public A, public B
{
public:
  using A::A;
  using B::B;
};


例如上面这个例子,这时我们就可以通过显式定义冲突的构造函数,这样就可以阻止隐式生成的继承构造函数导致的冲突。

class C : public A, public B
{
public:
  using A::A;
  using B::B;
  C(int c); // 显式声明冲突的构造函数
};

继承构造函数的优缺点

优点

通过这种继承构造函数的方式是隐式声明的。也就表示如果这个继承构造函数不使用,那么编译器就不会为其产生真正的函数。通过这个方式生成子类的各种构造函数比原有的方法更加简洁并且高效。


缺点

继承构造函数只能初始化父类中的成员,对于子类中成员变量只能通过C++11的初始化表达式进行设定默认值,具有一定的局限性。


如果父类的构造函数是在private作用域下,或者是子类虚继承父类,这种情况下就不能在子类中生成继承父类的构造函数了。而且子类一旦使用了继承构造函数,那么编译器就不会为子类生成默认的构造函数了。

目录
相关文章
|
27天前
|
编译器 C++ 开发者
【C++】继承
C++中的继承是面向对象编程的核心特性之一,允许派生类继承基类的属性和方法,实现代码复用和类的层次结构。继承有三种类型:公有、私有和受保护继承,每种类型决定了派生类如何访问基类成员。此外,继承还涉及构造函数、析构函数、拷贝构造函数和赋值运算符的调用规则,以及解决多继承带来的二义性和数据冗余问题的虚拟继承。在设计类时,应谨慎选择继承和组合,以降低耦合度并提高代码的可维护性。
32 1
【C++】继承
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
83 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
80 4
|
2月前
|
安全 程序员 编译器
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
90 11
|
2月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
63 1
|
2月前
|
C++
C++番外篇——虚拟继承解决数据冗余和二义性的原理
C++番外篇——虚拟继承解决数据冗余和二义性的原理
45 1
|
2月前
|
编译器 C语言 C++
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
29 1
|
2月前
|
安全 编译器 程序员
C++的忠实粉丝-继承的热情(1)
C++的忠实粉丝-继承的热情(1)
21 0
|
2月前
|
编译器 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
39 0
|
2月前
|
程序员 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
41 0