当前位置:   article > 正文

C++11之继承构造函数_is implicitly deleted because the default definiti

is implicitly deleted because the default definition would be ill-formed

1. 派生类无法继承基类构造函数

     当类B继承于类A的时候,它会继承类A中的数据成员与普通成员函数。但是某些成员函数是无法被继承下来的,比如类A(基类)中的合成构造函数(包括构造、析构、拷贝等等)。因此,类B在初始化类A的成员时候,需要显示调用类A的构造函数以达到初始化的目的。如下示例:

class A{
private:
    int m_a;
public:
    A(const int &a)
        :m_a(a)
    {}

    A(const A &a){}
};

class B : public A{
private:
    int m_b;
public:
    B(const int &a)
        :A(a)  //显示调用基类A的构造函数
    {}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

     类B显示调用类A的构造函数 A(a) 来初始化类A中的数据成员m_a。该示例中,类A仅有一个构造函数,若类A有多个构造函数,而类B(派生类)中仅增加了一些成员函数时, 我们会发现,类B中的所有操作(构造)函数都是为了初始化类A的成员, 这时候你会发现费了这么多精力,都耗在了初始化中,这或许并不是我们的初衷。

#include <iostream>
using namespace std;

class A{
private:
    int m_a;
public:
    A(const int &a, const double &b){}

    A(const int &a){}

    A(const float &f){}

    A(const double &d){}


    A(const A &a) = delete;
};

class B : public A{
private:
    int m_b;
public:
    B(const int &a, const double &b)
        :A(a, b)
    {}

    B(const int &a)
        :A(a)
    {}

    B(const float &f)
        :A(f)
    {}

    B(const double &d)
        :A(d)
    {}

    //NEW ADD
    void test(){}
};

int main()
{
    return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

     现在基类A有4个构造函数,派生类B没有新增成员,然而在派生类B中却不得不对基类A的各构造函数进行显示调用以达到初始化基类A中数据成员的目的。 在C++98前,这是迫不得已的,但是C+11中,我们可以使用 using 声明来解决该问题。
     

1.1 using 关键字

1.1.1 using 声明的使用

     using是c++中的一个关键字,它常常使用于以下3种场景,分别是:

     (1) 将命名空间(namespace)中的特定成员引入当前作用域 .

     (2) 将命名空间(namespace)的所有成员带入当前作用域 .

     (3) 将基类的方法(函数)或变量引入当前类的范围.

     using声明(using declaration)的使用形式如下:

                    using namespace::name;

     当然,using的作用范围不仅仅是这些,比如C++11中,使用using来进行别名声明(类似typedef的作用)。这个后面专门出一篇博文进行说明。

1.1.2 using 声明示例

1.1.2.1 将特定成员引入当前作用域

     将命名空间std中的数据成员cout和endl引入到当前main函数的作用域中。

#include <iostream>

int main()
{
    int a = 10;
    using std::cout;
    using std::endl;
    cout << "a: " << a << endl;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
1.1.2.2 将std 所有成员引入当前作用域

     将std命名空间中的所有成员引入当前作用域。

#include <iostream>
using namespace std;
int main()
{
    int a = 10;

    cout << "a: " << a << endl;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
1.1.2.3 将基类成员函数引入当前作用域
#include <iostream>
#include <string>
using namespace std;

class T
{
public:
    void t() {
        cout << "T ..."  << endl;
    }

    void h() {
        cout<<"T::h"<<endl;
    }
};

class U : public T{
public:
    using T::t;
    void t(const string &s) {
        cout << "U ... " << s << endl;
        t();
    }

    using T::h;
};

int main()
{
    U u;
    u.t("hello world.");

    U h;
    h.h();
    return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

     打印结果:

U ... hello world.
T ...
T::h
  • 1
  • 2
  • 3

1.1.3 using 声明使用范围

     注意,每条using声明引入一个数据成员或成员函数,可以在一行写1个using声明,也可以写多个声明,但是每个成员都需要有自己的using声明,并且以分号结束。eg:

     using T::f;                             //一行一条using声明
     using T::f; using T::m; using T::h; //一行多条using声明

     一条using声明可以出现在全局作用域、局部作用域、命名空间作用域及类的作用域中。

2. C++11使用using来继承基类构造函数

     基于using的这些功能特性,C++11对using的功能进行了扩展,使其基类的构造函数也支持继承。如下示例代码:

#include <iostream>
#include <string>
using namespace std;

class T
{
public:
   T() = default;
   T(const int &a){}
   T(const int &a, float &b){}
};

class U : public T{
public:
    using T::T;  //using声明基类T的构造函数
};

int main()
{
    U u;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

     现在派生类U中不需要再显示调用基类T的所有构造函数,通过using T::T,将基类T中的系列构造函数传递到派生类U中。注意:继承构造函数只能初始化基类中的数据成员,对于派生类中的数据成员,仍然需要自行处理,可以使用 快速初始化成员变量 新特性来进行初始化。如下:

class T
{
public:
   T() = default;
   T(const int &a){}
   T(const int &a, float &b){}
};

class U : public T{
public:
    using T::T;  //using声明基类T的构造函数

    int m_b{1};  //C++11新特性-快速初始化成员变量。
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3. 继承构造函数可能遇到的问题

     (1) 如果基类的构造函数是private属性,那么派生类无法声明基类的继承构造函数。

class T
{
private:   //基类构造函数声明为private
   T() = default;
   T(const int &a){}
   T(const int &a, float &b){}
};

class U : public T{
public:
    using T::T;  //using声明基类T的构造函数

    int m_b{1};
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

     当前基类T中构造函数修饰为private,此时派生类U中的继承构造函数声明无效,会报错:

D:\XXXX\XXX\main.cpp:13: 'U::U()' is implicitly deleted because the default definition would be ill-formed:
 class U : public T{
       ^
  • 1
  • 2
  • 3

     (2) 如果派生类是是以虚继承的方式从基类进行派生,在派生类中也无法声明基类的继承构造函数。

     (3) 当派生类是多继承方式时候,可能会出现继承类冲突隐患。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/209095
推荐阅读
相关标签
  

闽ICP备14008679号