多重继承类的复制构造函数

时间:2012-05-19 00:07:30

标签: c++ inheritance stl multiple-inheritance copy-constructor

考虑这段代码:

#include <vector>
#include <iostream>
using namespace std;

class Base
{
    char _type;
public:
    Base(char type):
        _type(type)
    {}

    ~Base() {
        cout << "Base destructor: " << _type << endl;
    }
};

class uncopyable
{
    protected:
        uncopyable() {}
        ~uncopyable() {}
    private:
        uncopyable( const uncopyable& );
        const uncopyable& operator=( const uncopyable& );
};

class Child : public Base, private uncopyable
{
    int j;
public:
    Child():
        Base('c')
    {}
    ~Child() {
        cout << "Child destructor" << endl;
    }
};


int main()
{
    vector<Base> v;
    Base b('b');
    Child c;

    v.push_back(b);
    v.push_back(c);
    return 0;
}

我系统的输出是:

Base destructor: b
Child destructor
Base destructor: c
Base destructor: b
Base destructor: b
Base destructor: c

我的问题是:

  • 为什么Base(带有类型b)的析构函数被调用三次而不是两次(我们是否有两个以上的对象b副本)?

  • 当我们复制Child类型的对象时会发生什么,考虑到其父级之一的复制构造函数是私有的。是不确定的行为?

  • 每当我尝试复制Child类型的对象时,我都希望得到编译时错误。我认为子的默认拷贝构造函数会尝试调用Uncopyable类的私有拷贝构造函数并导致编译错误。为什么不给出编译错误?

代码以这种方式设计的原因是因为Child类非常庞大。

每当客户端尝试复制Child对象(调用Child的析构函数而不调用Base的析构函数)时,所需的行为就会丢弃子数据。

这段代码实现了这一点,但我想它会导致未定义的行为并且内存泄漏(从不为复制的实例调用Child的析构函数)。

3 个答案:

答案 0 :(得分:9)

以下是您的代码中发生的事情:

int main() 
{ 
    vector<Base> v;    // 1
    Base b('b');       // 2
    Child c;           // 3

    v.push_back(b);    // 4
    v.push_back(c);    // 5
    return 0; 
}                      // 6
  1. 第1行:vector v construct

  2. 第2行:构建Base b(调用Base的构造函数)

  3. 第3行:构造子c(调用Child的构造函数和Base的构造函数)

  4. 第4行:v是最大容量的当前值,需要调整大小。
    内存由v分配给Base的1个元素 Base b复制到v [0](调用Base的复制构造函数)。

  5. 第5行:v再次处于最大容量,需要调整大小。
    内存由v分配给Base的2个元素 旧的v [0]被复制到新的v [0]中(调用Base的复制构造函数)。
    删除旧的v [0](调用Base的析构函数(“Base destructor:b”))。
    将子c复制到v [1](调用Base的复制构造函数)。

  6. 第6行:c,b和v超出范围。
    删除子c(调用Child的析构函数(“Child析构函数”),然后删除Base的析构函数(“Base destructor:c”)。
    基数b被删除(调用Base的析构函数(“Base析构函数:b”))。
    删除Base v [0],v [1](调用Base的析构函数两次(“Base destructor:b”,“Base destructor:c”))。

  7. 没有内存泄漏 - 对于上面序列中的每个构造函数,都会调用相应的析构函数。

    此外,您似乎对复制构造函数非常困惑。子c被传递给push_back作为Base&amp; - 然后按预期调用Base的复制构造函数。由于Base的隐式复制构造函数不是虚拟的或覆盖的,因此让Child从uncopyable派生不会改变它。

    请注意,vector<Base>无法存储Child类型的对象;它只知道为Base分配足够的内存。将Child实例分配给Base时发生的事情称为切片,虽然经常是无意识和误解,但在您描述的场景中看起来可能实际上是您想要的。

答案 1 :(得分:3)

  

每当我尝试复制Child类型的对象时,我都希望得到编译时错误。

您没有复制Child个对象。将Child c放入vector<Base>后,只会复制Base。它与执行b = c;基本相同。如果您复制/分配Child,则会收到错误。

Child d = c;  // compile error

默认的复制构造函数将调用任何基类和成员对象的复制构造函数,并对基元和指针执行按位复制。

答案 2 :(得分:0)

编辑:答案是错误的......目前正在编辑以获得更好的响应。

  

为什么Base(具有类型b)的析构函数被调用三次而不是两次(我们是否有两个以上的对象b副本)?

很可能是Vector正在制作b的副本。矢量经常这样做。

  

当我们复制Child类型的对象时会发生什么,考虑到其父类之一的copy-constructor是私有的。是不确定的行为?

没有。 C的拷贝构造函数将调用基类的拷贝构造函数。因此,如果基类复制构造函数是私有的,则它将无法编译。

  

每当我尝试复制Child类型的对象时,我都需要获得编译时错误,同时允许复制Base类对象。最好的方法是什么?

为Child声明一个私有的复制构造函数,如下所示:

private:
    Child(const Child& a) {
        throw "cannot make a copy";
    } 
  

每当客户端尝试复制Child对象(调用Child的析构函数而不调用Base的析构函数)时,所需的行为就是丢弃子数据。

不确定你的意思。复制构造函数意味着创建一个新对象。您无法对(旧)对象执行操作。

相关问题