从派生对象复制构造

时间:2019-02-13 06:21:01

标签: c++

在下面的代码中,函数foo是从派生对象d复制构造一个基础对象c。我的问题是:我们要得到确切的副本吗?因为我没有得到期望的多态行为

#include<iostream>

class Base
{
public:
    virtual void sayHello()
    {
        std::cout << "Hello Base" << std::endl ;
    }
};

class Derived: public Base
{
public:
    void sayHello() override
    {
        std::cout << "Hello Derived" << std::endl ;
    }
};

void foo(Base* d)
{

    Base* c = new Base(*d);
    c->sayHello() ;
}

int main()
{
    Derived d;
    foo(&d) ;  //outputs Hello Base
}



4 个答案:

答案 0 :(得分:2)

没有virtual构造函数,也没有复制构造函数。

但是,可以定义一个行为类似的函数。

在我的情况下,这是我添加到OP示例中的virtual成员函数copy()

#include <iostream>
class Base
{
public:
    virtual Base* copy() const { return new Base(*this); }

    virtual void sayHello()
    {
        std::cout << "Hello Base" << std::endl ;
    }
};

class Derived: public Base
{
public:
    virtual Base* copy() const override { return new Derived(*this); }

    void sayHello() override
    {
        std::cout << "Hello Derived" << std::endl ;
    }
};

void foo(Base* d)
{

    Base* c = d->copy();
    c->sayHello() ;
}

int main()
{
    Derived d;
    foo(&d) ;  //outputs Hello Derived
    return 0;
}

输出:

Hello Derived

Live Demo on coliru

缺点是Base的每个派生类都必须提供它才能使其正常运行。 (我不知道如何用任何技巧说服编译器为我进行检查。)

部分解决方案可能是使copy()中的class Base纯虚拟(假设它不是可实例化的)。

答案 1 :(得分:1)

您可能会更改新行

Base* c = new Derived(*d);

,因此在Base指针中具有Derived类型。在运行时,将查找它的类型,并获得正确的输出。

让我知道我是否做错了...只是在飞行过程中突然想到的。

答案 2 :(得分:1)

要回答有关是否是复制构造的问题,请添加一些成员。 Base将有一个成员,m_bDerived将继承m_b但也有另一个成员m_d

#include <iostream>

struct Base {
    const int m_b;
    Base() = delete;
    Base(const int a_b) : m_b(a_b) {}
    virtual void sayHello() {
        std::cout << "Base " << m_b << std::endl;
    }
};

struct Derived : public Base {
    const int m_d;
    Derived() = delete;
    Derived(const int a_b, const int a_d) : Base(a_b), m_d(a_d) {}
    void sayHello() override {
        std::cout << "Derived " << m_b << ' ' << m_d << std::endl;
    }
};

void foo(Derived* a) {
    Base* b = new Base(*a);
    b->sayHello(); // Output is "Base 1", 1 was copied from argument a
}

void bar(Derived* a) {
    Base* d = new Derived(*a);
    d->sayHello(); // Output is "Derived 1 2"
}

int main() {
    Derived d(1, 2);
    foo(&d);
    bar(&d);
    return 0;
}

该行:

Base* b = new Base(*a);

创建了Base,因此sayHello调用了Base的实现,而该实现不了解m_d。但是,此行确实从派生类中复制了m_b

该行:

Base* d = new Derived(*a);

创建了Derived,因此sayHello调用了Derived的实现,该实现复制了m_bm_d

答案 3 :(得分:0)

Base类指针指向Derived类对象时,将出现预期的多态行为。然后在运行时,将检查Base类指针指向的对象的实际类型,并调用适当的函数。

Base* c = new Base(*d); // <<-- case of object slicing

在这里,c指向Base类对象。因此,c->sayHello() ;势必在运行时调用Base::sayHello()

are we getting an exact copy?。否,因为由于Base而创建了new Base对象。由于对象切片,Base对象的*d部分被传递到copy c'tor,并且您得到的是对应的Base对象。

Base *c = new Derived(*d);将给出预期的行为。