上一篇:《深入理解C++11》笔记–模板
这篇文章介绍的了第三章中构造函数相关的内容。提前说明,许多编译器会多拷贝构造和移动构造进行优化省略,这样就看不到拷贝构造和移动构造的过程,需要在编译器选项中设置-fno-elide-constructors来关闭优化。
继承构造函数
派生类的构造函数往往需要调用基类的构造函数,例如:
class Base{
public:
Base(int a): i(a){}
private:
int i;
};
class Inherit:public Base{
public:
Inherit(int a): Base(a){}
};
但是,如果基类有多中构造函数的情况下,派生类如果要覆盖所有的构造函数,就必须也实现对应个数的构造函数,这时候会感觉很麻烦,而继承构造函数就能解决这个问题。
C++原来能使用using修饰符来对基类的成员函数进行继承,就是说派生类对象进行继承后能调用基类的成员函数。
class Base{
public:
void test(double i){std::cout << "Base : " << i <<std::endl;}
};
class Inherit:public Base{
public:
using Base::test; // 继承了成员函数
void test(int i){std::cout << "Inherit : " << i <<std::endl;}
};
int main()
{
Base base;
base.test(1.1); // Base:1.1
Inherit in;
in.test(1); // Inherit:1
in.test(1.1); // Inherit:1.1 (如果没有继承基类的成员函数,1.1将会被隐式转换成int型)
return 0;
}
C++11中,构造函数也支持了继承。
class Base{
public:
Base(int i){std::cout << "Base : " << i <<std::endl;}
};
class Inherit:public Base{
public:
using Base::Base;
};
int main()
{
Base base(1); // Base : 1
Inherit in(1); // Base : 1
getchar();
return 0;
}
委派构造函数
实际编程过程中,可能会遇到一种情况:一个类有多个构造函数,并且这些构造函数中都有一段相同的代码,这种情况下会有到大量的重复代码。
class Base{
public:
Base(){ some_code; }
Base(int i){ some_code; }
Base(int i, int j){ some_code; }
};
原来,我们可能会通过将some_code都放到一个成员函数中来解决代码重复的问题。
class Base{
public:
Base(){ init(); }
Base(int i){ init(); }
Base(int i, int j){ init(); }
private:
void init() { some_code; }
};
但是这样会有一个问题:init函数有可能在其他地方误用导致一些问题。C++11中新增了委派构造函数的特性,可以解决以上问题,构造函数能够在其他构造函数的初始化列表中被调用,这样就不会有误调用的情况产生。
class Base{
public:
Base(){ some_code; } // 被委派
Base(int i):Base(),data(i){ } // 编译失败
Base(int i, int j):Base(){ }
private:
int data;
};
另外,需要注意的是,使用了委派构造函数之后就不能在初始化列表中初始化其他成员变量,只能在函数中进行赋值。但是,这样的话成员变量的初始化又有些不完美。通常,建议被委派的构造函数尽可能的通用,看以下例子:
class Base{
public:
Base():Base(0,0){ }
Base(int i):Base(i, 0){ }
private:
Base(int i, int j):data(i){ some_code; } // 被委派
int data;
};
委派构造函数通常还能用于模板构造函数,来让多种数据类型都适用。
class Base{
public:
Base(std::vector<int> vec):Base(vec.begin(), vec.end()){} // 可以是vector,也可以是queue等
private:
std::list<int> listData;
template<typename T> Base(T first, T last) : listData(first, last){}
};
最后,说明一下委派构造函数中的异常捕捉。以下的代码,能够在被委派的构造函数抛出异常时捕捉到异常,并跳过正常的初始化流程。
class Base{
public:
Base() try:Base(0,0)
{
std::cout << "some_code" << std::endl;
}
catch(...)
{
std::cout << "error" << std::endl;
}
private:
Base(int i, int j):data(i){ throw 0; } // 被委派
int data;
};