Welcome

首页 / 软件开发 / C# / 拷贝构造和赋值操作符,C#和本机C++代码的互用性

拷贝构造和赋值操作符,C#和本机C++代码的互用性2011-07-10 vckbase NorthTibet我有一个简单的 C++ 问题。我想让我的拷贝构造函数和赋值操作做同样的事情。你能告诉我最佳实现方法吗?

Shadi Hani乍一看, 这似乎是一个答案简单的简单问题:写一个调用 operator= 的构造函数不就行了:CFoo::CFoo(const CFoo& obj)
{
*this = obj;
}

或者,写一个公用的拷贝方法,拷贝构造函数和 operator= 都调用这个方法也行。就像这样:

CFoo::CFoo(const CFoo& obj)
{
CopyObj(obj);
}
CFoo& CFoo::operator=(const CFoo& rhs)
{
CopyObj(rhs);
return *this;
}

对于大多数类来说,这是行得通的,但还有些 特殊情况需要考虑。如果你的类包含有数据成员是另一个类的实例会怎样呢?为了弄清楚这个问题,我写了一个测试程序如 Figure 1 所示。 它有一个主类 CMainClass,它包含另一个类 CMember 的实例。两个类都有拷贝构造函数和赋值操作,用 CMainClass 的拷贝构造函数调用 operator=,如下面的代码段所示。代码中使用 printf 语句是为了显示何时调用了哪个方法。为了运行构造函数,cctest 程序首先用缺省构 造函数创建 CMainClass 实例,然后用拷贝构造函数创建另一个实例:

CMainClass obj1;
CMainClass obj2 (obj1);

如果你编译并运行 cctest,当构造 obj2 时,你会看到下面的 printf 信息:

CMember: default ctor
CMainClass: copy-ctor
CMainClass: operator=
CMember: operator=

成员对象 m_obj 被初始化了两次!第一次是缺 省构造,第二次是赋值时再次被初始化。嘿,这是怎么回事?

在 C++ 中,赋值和拷贝是不同的,因为拷贝构造函数是对未初始化的内 存进行初始化操作,而赋值是对现有的已经初始化的对象进行操作。如果你的类包含其它的类实例作为数据成员,那么拷贝构造在调用 operator=之前必须首先构造这些数据成员。其结果是致使这些成员就像 cctest 那样被初始化两次,明白了吗?当你用赋值操作而不是初始化 例程进行成员初始化时,缺省构造函数也会发生同样的事情。例如:

CFoo::CFoo()
{
m_obj = DEFAULT;
}

与下面代码相对:

CFoo::CFoo() : m_obj(DEFAULT)
{
}

使用赋值方式,m_obj 被初始化两 次,而用初始化例程语法,m_obj 只被初始化一次。所以,要如何避免拷贝构造期间额外的初始化呢?当它与你的代码重用初衷相抵触时, 最 好的解决俄u方法就是分开实现拷贝构造和赋值操作,即便它们做同样的事情。从拷贝构造中调用 operator= 肯定能行得通,但不是最有效率 的实现。我对初始化的建议是:

CFoo::CFoo(const CFoo& rhs) : m_obj(rhs.m_obj) {}

现在,主拷贝构造用初 始化例程调用成员对象的拷贝构造,并且 m_obj 只被其拷贝构造初始化一次。通常情况下,拷贝构造应该调用其成员的拷贝构造。赋值也是如 此。并且,它也同样适用于基类:派生类的拷贝构造和赋值操作应该调用对应的基类方法。当然,有时因为一些具体情况,可能你的做法会有 所不同——这里我所描述的是通用规则,只有在你遇到强制性原因时才会破坏这个规则。如果你要在基本对象被初始化之后完成一 些公共任务,可以将它们放到一个公共的初始化方法中,并在构造函数和 operator= 中调用。