C++中,一个参数的构造函数(或者除了第一个参数外其余参数都有缺省值的多参构造函数),承担了两个角色。
例如一个类A的构造函数A(int i)就是,既可以用来作为构造器,又可以实现隐式转换A a=1;因为1可以通过构造函数A(int i)转换为一个类A的对象。
#include <iostream>
class Base
{
public :
Base(const char data)
{
std::cout <<"constructor..." <<std::endl;
this->m_data = data;
}
protected :
char m_data;
};
int main(void)
{
Base base1('a'); // 用于构建单参数的类对象
Base base2 = 'b'; // 隐含的类型转换操作符
return 0;
}
这种方法看起来很方便,但是有时这并不是我们想要的,但是由此可能引入一些其他问题。
比如如下的代码就可以正常运行
Base base = 10000; // 由于10000可以被转成char类型
因此C++提供关键字explicit
,可以阻止不应该允许的经过转换构造函数进行的隐式转换发生.
特别对于如下代码
#include <iostream>
#include <cstring>
class Base
{
public :
Base(const char *str)
{
std::cout <<"constructor..." <<std::endl;
this->m_length = strlen(str);
this->m_cstr = new char[this->m_length + 1];
strcpy(this->m_cstr, str);
}
Base(const Base &base)
{
this->m_length = base.m_length;
this->m_cstr = new char[this->m_length + 1];
strcpy(this->m_cstr, base.m_cstr);
}
~Base()
{
delete this->m_cstr;
}
protected :
char *m_cstr;
int m_length;
};
int main(void)
{
Base base = (char *)10000; // 隐含的类型转换操作符
return 0;
}
声明为explicit
的构造函数不能在隐式转换中使用,只能显示调用,去构造一个类对象。
Copy constructor
也是同样的,如果Copy constructor
被声明为explicit
,则这个类对象不能用于传参和函数返回值。但是仍然可以直接调用。
我们都知道,在C++中,拷贝构造函数的调用时机有如下三种情况
#include <iostream>
class Base
{
public :
Base( )
{
//std::cout <<"default constructor..." <<std::endl;
this->m_data = '\0';
}
Base(char data)
{
//std::cout <<"simple constructor..." <<std::endl;
this->m_data = data;
}
Base(const Base &base)
{
std::cout <<"copy constructor..." <<std::endl;
this->m_data = base.m_data;
}
protected :
char m_data;
};
// 参数为Base对象
void Func(const Base base)
{
}
// 返回值为base对象
Base Func( )
{
Base base;
return base;
}
void Copy()
{
Base b1;
Base b2 = b1;
}
int main(void)
{
Base base(10);
std::cout <<std::endl <<"参数为Base对象..." <<std::endl;
Func(base); // 优化未优化情况下均调用1次拷贝构造函数
std::cout <<std::endl <<"返回值为base对象[可被优化]..." <<std::endl;
Func(); // 优化情况下调用0次, 未优化情况下调用1次[函数返回时调用]
Base b = Func(); // 优化情况下调用0次, 未优化情况下调用2次[函数返回时调用 + 初始化类型b时调用]
std::cout <<std::endl <<"用base对象初始化另外一个对象..." <<std::endl;
Copy(); // 优化未优化情况下均调用1次拷贝构造函数
return 0;
}
但是其实很多现代编译器的返回值优化技巧,返回值时的情况会被优化掉,但是这个并不是我们今天讨论的重点。
具体可参见
RVO-编译器返回值优化
在使用GNU/g++编译器时可以使用”-fno-elide-constructors”选项来强制g++总是调用copy构造函数,即使在用临时对象初始化另一个同类型对象的时候(主要是返回值为)
explicit
对拷贝构造函数也会限制作用,将会阻止隐式拷贝构造函数的调用。
explicit对拷贝构造函数也会限制作用,将会阻隐式拷贝构造函数的调用.
将拷贝构造函数声明为explicit,则会阻止隐式拷贝构造函数的调用。
这时候我们可以将参数传递或者函数返回修改为引用的方法,而初始化的时候采用显式调用拷贝构造函数。
#include <iostream>
class Base
{
public :
Base( )
{
//std::cout <<"default constructor..." <<std::endl;
this->m_data = '\0';
}
explicit Base(char data)
{
//std::cout <<"simple constructor..." <<std::endl;
this->m_data = data;
}
explicit Base(const Base &base)
{
std::cout <<"copy constructor..." <<std::endl;
this->m_data = base.m_data;
}
protected :
char m_data;
};
// 参数为Base对象
void Func(const Base& base) // 传入引用对象
{
}
// 返回值为base对象
Base& Func( )
{
Base base;
return base; // 返回引用对象
}
void Copy()
{
Base b1;
Base b2(b1); // Base b2 = b1显示调用拷贝构造函数
}
int main(void)
{
Base base(10);
std::cout <<std::endl <<"参数为Base对象..." <<std::endl;
Func(base); // 优化未优化情况下均调用1次拷贝构造函数
std::cout <<std::endl <<"返回值为base对象[可被优化]..." <<std::endl;
Func(); // 优化情况下调用0次, 未优化情况下调用1次[函数返回时调用]
//Base b(Func()); 返回局部对象
std::cout <<std::endl <<"用base对象初始化另外一个对象..." <<std::endl;
Copy(); // 优化未优化情况下均调用1次拷贝构造函数
return 0;
}
C++中,一个参数的构造函数(或者除了第一个参数外其余参数都有缺省值的多参构造函数),承担了两个角色。
声明为explicit
的构造函数不能在隐式转换中使用,只能显示调用,去构造一个类对象。
explicit
关键字只对有一个参数的类构造函数有效, 如果类构造函数参数大于或等于两个时, 是不会产生隐式转换的, 所以explicit
关键字也就无效了
但是将拷贝构造函数声明成explicit
并不是良好的设计,一般只将有单个参数的constructor
声明为explicit
,而copy constructor
不要声明为explicit
.