explicit C++

来源:互联网 发布:微博有mac版 编辑:程序博客网 时间:2024/05/20 02:21

一个只带一个参数,且为非explicit的构造函数,充当两个角色(http://baike.baidu.com/view/2422253.htm)。一是一个构造器;二是隐式类型转换器,即把构造函数的参数类型对象隐式转换为该类对象。如果把构造函数声明为explicit,那么就会屏蔽第二个作用。

例如:

class Complex{private:    double real;    double imag; public:        Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}         bool operator == (Complex rhs) {       return (real == rhs.real && imag == rhs.imag)? true : false;    }}; int main(){      Complex com1(3.0, 0.0);     if (com1 == 3.0)       cout << "Same";    else       cout << "Not Same";     return 0;}
输出:same

因为构造函数的参数有默认值,所以可以看作是只有一个参数。那么就存在一个隐式的转换,在必要的时候把double类型的数据转换为Complex类型的对象。所以 if(com1 == 3.0)会隐式地调用构造函数把3.0转换成一个Complex对象,假设该匿名对象为c,那么c是由Complex(double=0.0,double=0.0)构造出来的,所以c=(3.0,0.0)。

如果在构造函数前加上explicit关键字,那么3.0就不能够隐式地转换为Complex对象。当然,可以进行显示类型转换,如下的代码是正确的,且输出也为same

class Complex{private:    double real;    double imag; public:        explicit Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}         bool operator == (Complex rhs) {       return (real == rhs.real && imag == rhs.imag)? true : false;    }}; int main(){      Complex com1(3.0, 0.0);     if (com1 == (Complex)3.0)       cout << "Same";    else       cout << "Not Same";     return 0;}
问题1:为什么上面的 bool operator==(Complex rhs) 换成 bool operator==(Complex& rhs),就编译通不过,上面两个都通不过?

问题1的解释是:隐式转换得到的对象是一个右值,不能被改变。C++规定,当参数为引用传递时,non-const可以传递给const,const不能传递给non-const。上述问题就是因为隐式转换得到的对象为const,而形参为non-const,所以出错。其实改成 bool operator==(const Complex& rhs); 就没问题(已验证)。

还有下面问题

class A{public:A(int i = 0){ data = i;}A(const A& a){ data = a.data + 1;}int data;};
A a(A(1)); 

A a = A(1);

问题2:上面两个语句并没有调用拷贝构造函数,a.data仍然是1,为什么?

问题2可能是因为,编译器作了一些优化,只执行了第一个构造函数。见 http://zh.wikipedia.org/wiki/%E8%A4%87%E8%A3%BD%E5%BB%BA%E6%A7%8B%E5%AD%90

虽然没有执行拷贝构造函数,但是重载时还是不能写成A(A& a)。如下代码中A a1(A(1));依然是错的,必须把拷贝构造函数定义成 A(const A& a);

class A{public:A(int i = 0){ data = i;}A(A& a){ cout << "A(const A& a)" << endl;}int data;};void f(const A& a){}int main(){<span style="white-space:pre"></span>A a1(A(1));// 当A(int)与A(const A&)为non-explicit时,等价于 A a1 = A(1); 或 A a1 = 1;cout << a1.data << endl;    <span style="white-space:pre"></span>return 0;}
问题3:为什么当拷贝构造函数为explicit时,A a = a1;编译通不过?如下代码编译通不过

问题3可能的解释为:explicit构造函数不仅禁止类型的默认转换,还禁隐式地调用该构造函数。http://www.tuicool.com/articles/2ANrQbj

问题3代码延伸,下面代码编译通过,且输出为 A(const A& a)

class A{public:A(int i){ data = i;}A(const A& a){ cout << "A(const A& a)" << endl;}explicit A(A& a){ cout << "explicit A(A& a)" << endl;}int data;};int main(){A a1 = 1;A a2 = a1;        return 0;}

那么综上可以做这样的总结:

每一个单参数的non-explicit构造函数,都定义了一个隐式转换,把构造函数参数类型的数据转换成该类对象。当构造函数被声明为explicit时,该构造函数不能参与到隐式转换中去,同时也不支持隐式调用该构造函数。

explicit构造函数的作用就是抑制隐式转换与隐式调用,隐式调用主要是问题3中的情况

explicit构造函数可以参与显示转换,比如强制转换:(type)value,static_cast等等

Q:什么情况下构造函数要声明为explict,什么情况下不需要?

A:把构造函数声明为explicit主要是为了防止隐式转换,这样会降低代码的可阅读性。比如上面的Complex,如果构造函数未声明为explicit,这样的语句Complex c = 3.0则会编译通过,但是很难被程序员理解,不知道3.0到底是real还是image。一般情况下,转换构造函数要声明为explicit,当然,有些情况下,转换构造函数不能声明为explicit。比如在list与deque中,const_iterator应该可以由iterator构造的,因为下面的这个语句从需求上来说应该被接受const_iterator cit = begin();begin()返回的是一个iterator,那么在构造cit时就会调用const_iterator(const iterator& it)把begin()转换成一个const_iterator,然后再调用拷贝构造函数。如果该转换构造函数被声明为explicit,则上面的语句就会编译出错,用户就会觉得很奇怪。






0 0