概念
继承构造函数在C++11特性中随之提及,其大概可以理解为:
解决了派生类无法直接使用基类中的构造函数的这一问题。
正常情况下,基类定义了自己的构造函数,派生类公有继承基类后,由于派生类隐藏了基类的同名函数,是无法直接使用基类的同名函数的(这里针对于普通函数)。如下所示的例子:
基类Base的定义
class Base
{
public:
Base(int a):m_a(a) {}
Base(int a, double b):m_a(a),m_b(b) {}
Base(int a, double b, string c) :m_a(a), m_b(b), m_c(c) {}
private:
int m_a;
double m_b;
string m_c;
};
派生类Derived
class Derived :public Base
{
public:
private:
};
上述的派生类Derived 中没有定义自己的构造函数,虽然是公有继承的基类Base,但是不能直接使用基类的构造函数构造自己的对象。像下面这样是错误的,无法编译通过:
在主函数中的使用:
int main()
{
Derived d(12,4.56,"");//编译报错,没有相应的构造函数
}
衍生问题
上述也引发一个问题,若是像下面这样写,也是不正确的。
int main()
{
Derived d;//编译报错,尝试引用已删除的函数
}
Derived 类中没有写任何函数,直接使用派生类默认的构造函数。按理这里应该是会调用自己的默认构造函数,但编译报错:尝试引用已删除的函数。这是因为派生类继承基类之后,会将派生类默认生成的构造函数删除吗?求解!!!
原因:Derived 类创建对象d时,先调用基类构造函数,再调用派生类构造函数,基类中已经显式的定义了三个有参构造函数,系统会将默认的无参构造函数删除,所以创建Derived 对象d的时候,调用基类构造函数,没有找到匹配的无参构造函数,已经被系统删除了。
但是改为下面这样:
int main()
{
Derived d();//编译通过,但有警告:未调用原型函数(是否是有意用变量定义的?)
}
这是为什么呢????
原因:单独将Derived d();当作一个函数的声明。
同样如果是一个没有继承关系的类,可以这样创建类对象:
class Base
{
public:
private:
};
int main()
{
Base d();//编译通过,这里也可以这样写 Base d;同样也会编译通过
}
创建对象时,调用的默认的无参构造函数。编译器会为没有显示创建构造函数,析构函数,拷贝构造函数(复制构造函数)以及赋值构造函数(赋值运算符重载)的类自动生成这些函数。
使用示例
下面是继承构造函数的使用。也就是派生类可以调用基类的构造函数的来创建自己的对象的方式。
依旧是最初的例子:
class Base
{
public:
Base(int a):m_a(a) {}
Base(int a, double b):m_a(a),m_b(b) {}
Base(int a, double b, string c) :m_a(a), m_b(b), m_c(c) {}
private:
int m_a;
double m_b;
string m_c;
};
class Derived :public Base
{
public:
using Base::Base;//使用基类的构造函数
private:
};
int main()
{
//使用基类的构造函数
Derived d(12,4.56,"");
Derived e(2);
Derived f(4,5.67);
}
此例中派生类可以使用基类的构造函数来创建对象。方法是在派生类中采用using 声明的方式。
像该例中的using Base::Base;使用时就是:
using 类名::函数名;
注意
以上继承构造函数在使用时,若是派生类中定义了自己的成员变量,继承构造函数不会去初始化派生类定义的成员变量。且上述using 类名::函数名; 调用基类构造函数的方法也适用于函数。
扩展使用(函数)
上面提到这种using声明的方式也可以在函数上使用,下面便是一个示例。
示例代码
依旧采用文章中提到的示例代码稍作修改。
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
Base(int a):m_a(a) {}
Base(int a, double b):m_a(a),m_b(b) {}
Base(int a, double b, string c) :m_a(a), m_b(b), m_c(c) {}
void Fun() { cout << "Base::Fun()" << endl; }
private:
int m_a;
double m_b;
string m_c;
};
class Derived :public Base
{
public:
using Base::Base;//使用基类的构造函数
void Fun(int a) { cout << "Derived::Fun()" << endl; }//隐藏
private:
};
int main()
{
//使用基类的构造函数
Derived d(12,4.56,"");
Derived e(2);
Derived f(4,5.67);
//使用基类的函数
d.Fun();
}
可以看到在使用继承构造函数的基础上,在基类和派生类中新增了函数Fun,两个函数分别位于基类与派生类中,函数名相同,参数不同,此为隐藏关系。更多关于隐藏的介绍可以查看我之前的博文,搜索关键字——隐藏。
这里通过d.Fun()调用父类的函数Fun(),但是整个代码是无法通过编译的,要想通过派生类对象调用父类的同名函数需要使用相应的using 类名::函数名来声明。
下面稍作修改,是正确的代码:
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
Base(int a):m_a(a) {}
Base(int a, double b):m_a(a),m_b(b) {}
Base(int a, double b, string c) :m_a(a), m_b(b), m_c(c) {}
void Fun() { cout << "Base::Fun()" << endl; }
private:
int m_a;
double m_b;
string m_c;
};
class Derived :public Base
{
public:
using Base::Base;//使用基类的构造函数
using Base::Fun;//使用基类的同名函数
void Fun(int a) { cout << "Derived::Fun()" << endl; }//隐藏
private:
};
int main()
{
//使用基类的构造函数
Derived d(12,4.56,"");
Derived e(2);
Derived f(4,5.67);
//使用基类的函数
d.Fun();
d.Base::Fun();//与上一行写法都可以,有两种调用方式
e.Fun(2);//使用派生类自己的函数
}
上述代码可以编译通过。
输出结果
输出结果为:
注意
在派生类中引入using声明之后,d.Fun();与d.Base::Fun();都可以达到调用基类的Fun()函数的作用。
在没有引入using声明时,可以采用d.Base::Fun();这种调用方式。d.Base::Fun();的调用已经指明了调用的是基类中的函数。
下面是不引入using声明的基类同名函数Fun()调用示例:
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
Base(int a):m_a(a) {}
Base(int a, double b):m_a(a),m_b(b) {}
Base(int a, double b, string c) :m_a(a), m_b(b), m_c(c) {}
void Fun() { cout << "Base::Fun()" << endl; }
private:
int m_a;
double m_b;
string m_c;
};
class Derived :public Base
{
public:
using Base::Base;//使用基类的构造函数
void Fun(int a) { cout << "Derived::Fun()" << endl; }//隐藏
private:
};
int main()
{
//使用基类的构造函数
Derived d(12,4.56,"");
Derived e(2);
Derived f(4,5.67);
//使用基类的函数
d.Base::Fun();//编译正确
}