类似这样的问题千奇百怪。例如:
-
- 为什么我明明不想复制对象,而编译器却偏偏这么做了呢?
- 如何关闭复制机制?
- 如何防止隐式转换?
- 为何 int 自动转换成了复数?
struct Point {
int x,y;
Point(int xx = 0, int yy = 0) :x(xx), y(yy) { }
};
Point p1(1,2);
Point p2 = p1;
至此,p2.x==p1.x 并且 p2.y==p1.y。这可能正是你想要的(而且也是为了和 C 兼容所必需的),但是,以下代码:
class Handle {
private:
string name;
X* p;
public:
Handle(string n)
:name(n), p(0) { /* acquire X called "name" and let p point to it */ }
~Handle() { delete p; /* release X called "name" */ }
// ...
};
void f(const string& hh)
{
Handle h1(hh);
Handle h1 = h2; // 会引起灾难!
// ...
}
在此,默认复制构造函数使得 h2.name==h1.name 并且 h2.p==h2.p。这将导致一场灾难:当函数 f() 运行结束时,会调用 h1 和 h2 的析构函数,这就导致 h1.p 和 h2.p 所指向的对象被 delete 了两次。
如何避免这场灾难?最简单的办法是,将复制构造函数和赋值运算符声明为私有成员,从而关闭复制机制:
class Handle {
private:
string name;
X* p;
Handle(const Handle&); // 阻止复制
Handle& operator=(const Handle&);
public:
Handle(string n)
:name(n), p(0) { /* acquire the X called "name" and let p point to it */ }
~Handle() { delete p; /* release X called "name" */ }
// ...
};
void f(const string& hh)
{
Handle h1(hh);
Handle h1 = h2; // 编译器报错
// ...
}
如果需要复制机制,我们可以定义自己的复制构造函数和赋值运算符,让它们按我们期待的那样工作。
现在回过头来再看看类 Point。对 Point 来说,可以使用默认的复制机制,但它的构造函数有点问题:
struct Point {
int x,y;
Point(int xx = 0, int yy = 0) :x(xx), y(yy) { }
};
void f(Point);
void g()
{
Point orig; // 使用默认值 (0,0) 创建 orig
Point p1(2); // 使用 yy 的默认值 (0) 来创建 p1
f(2); // 调用 Point(2,0);
}
为了便于创建对象(如这里的 orig 和 p1),我们为 Point 的构造函数提供了默认参数。然后,有些人会感到惊讶的事情发生了:调用 f() 时,2 会转换成 Point(2,0)。当我们定义一个接受单个参数的构造函数时,同时亦定义了一种类型转换方式。默认情况下,类型转换是隐式进行的。若想把类型转换改成 显式进行,就要将构造函数声明为 explicit:
struct Point {
int x,y;
explicit Point(int xx = 0, int yy = 0) :x(xx), y(yy) { }
};
void f(Point);
void g()
{
Point orig; // 使用默认值 (0,0) 创建 orig
Point p1(2); // 使用 yy 的默认值 (0) 来创建 p1
// 显式调用构造函数
f(2); // 错误(试图进行隐式转换)
Point p2 = 2; // 错误(试图进行隐式转换)
Pont p3 = Point(2); // 正确(显式转换)
}
原文地址:http://www.research.att.com/~bs/bs_faq2.html#explicit-ctor