C++构造与析构(17) - virtual拷贝构造函数

在上一篇文章中介绍了实现"virtual构造函数"的方法--《C++构造与析构(16) - virtual构造函数》

有没有可能在不知道类的具体类型的情况下创建一个对象?

众所周知,拷贝构造函数是用一个已经存在的对象,来构造一个新的对象。新对象的初始状态取决于已存在对象的状态。当使用一个对象初始化另一个对象时,编译器会调用拷贝构造函数。但是,编译器需要知道确切的类型信息才能调用拷贝构造函数。

#include <iostream>
using namespace std;

class Base {
public:
};

class Derived : public Base {
public:
    Derived() { cout << "Derived created" << endl;  }
    Derived(const Derived& rhs) { cout << "Derived created by deep copy" << endl; }
    ~Derived() { cout << "Derived destroyed" << endl; }
};

int main() {
    Derived s1;
    Derived s2 = s1;  // 编译器会调用拷贝构造函数
                      // s1和s2的类型对于编译器是具体的/确切知道的。
    // 基于基类指针或引用(指向的是子类对象),如何创建Derived1或Derived2对象?
    return 0;
}

运行结果:
Derived created
Derived created by deep copy
Derived destroyed
Derived destroyed

如果用户无法确定对象的类型,此时如何使用拷贝构造函数?例如virtual构造函数在运行时期间创建了一个对象。当使用拷贝构造函数创建对象时,如果已存在对象是virtual构造函数创建的,我们无法使用拷贝构造函数。需要自定义一个在运行时可以拷贝对象的函数。

举例说明:有一个绘图程序,它支持从已有的图案进行拷贝-粘贴。
从程序员的角度看,我们无法知道哪个图案会被拷贝-粘贴,因为它是一个运行时的行为。在这种情况下,就需要使用virtual拷贝构造函数来帮助。

类似地,对于一个记录板程序,从已存在对象拷贝,然后粘贴。具体对象类型是运行时的行为,无法提前知道。就需要使用virtual拷贝构造函数了。
参见下面例子:

#include <iostream>
using namespace std;

class Base {
public:
    Base() {}
    virtual ~Base() {}  // 确保调用子类析构函数
    virtual void ChangeAttributes() = 0;
    static Base* Create(int id);  // 做为"virtual constructor"
    virtual Base* Clone() = 0;  // 做为"virtual copy constructor"
};

class Derived1 : public Base {
public:
    Derived1() {  cout << "Derived1 created" << endl;  }
    Derived1(const Derived1& rhs) { cout << "Derived1 created by deep copy" << endl; }
    ~Derived1() { cout << "~Derived1 destroyed" << endl; }
    void ChangeAttributes() { cout << "Derived1 Attributes Changed" << endl; }
    Base* Clone() { return new Derived1(*this); }
};

class Derived2 : public Base {
public:
    Derived2() { cout << "Derived2 created" << endl; }
    Derived2(const Derived2& rhs) { cout << "Derived2 created by deep copy" << endl; }
    ~Derived2() { cout << "~Derived2 destroyed" << endl; }
    void ChangeAttributes() {cout << "Derived2 Attributes Changed" << endl; }
    Base* Clone() { return new Derived2(*this); }
};

class Derived3 : public Base {
public:
    Derived3() { cout << "Derived3 created" << endl; }
    Derived3(const Derived3& rhs) { cout << "Derived3 created by deep copy" << endl; }
    ~Derived3() { cout << "~Derived3 destroyed" << endl; }
    void ChangeAttributes() { cout << "Derived3 Attributes Changed" << endl; }
    Base* Clone() { return new Derived3(*this); }
};

Base* Base::Create(int id) {
    //如果新的Derived类加入,只需要在此添加if-else. 
    //User类不需要重新编译即可支持新加的对象。
    if (id == 1) { return new Derived1; }
    else if (id == 2) { return new Derived2; }
    else { return new Derived3; }
}

class User {
public:
    // 运行时期间创建Base对象
    User() : pBase(0) {
        int input;
        cout << "Enter ID (1, 2 or 3): ";
        cin >> input;
        while ((input != 1) && (input != 2) && (input != 3)) {
            cout << "Enter ID (1, 2 or 3 only): ";
            cin >> input;
        }
        // 调用"Virtual Constructor"来创建对象
        pBase = Base::Create(input);
    }
    ~User() {
        if (pBase) {
            delete pBase;
            pBase = 0;
        }
    }
    void Action() {
        // 赋值当前对象
        Base* pNewBase = pBase->Clone();
        pNewBase->ChangeAttributes();
        delete pNewBase;
    }
private:
    Base* pBase;
};

int main() {
    User* user = new User();
    user->Action();
    delete user;
}

运行结果:
Enter ID (1, 2 or 3): 3
Derived3 created
Derived3 created by deep copy
Derived3 Attributes Changed
~Derived3 destroyed
~Derived3 destroyed

User类使用virtual constructor创建一个对象。这个对象取决于用户输入值input ID。
函数Action()会拷贝已存在对象给新对象,并修改新对象的属性。使用虚函数Clone()来创建新对象的过程,即被称为virtual copy constructor。
这里使用Clone()方法的这种概念,就属于设计模式中的 原型模式prototype pattern。具体可参考本人的这篇文章 《设计模式(4) - Prototype原型模式》

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值