2015-11-14 71 views
3

我正在實現virtual clone()方法,該方法可能與返回對象的「實際」類型的例外完全相同,並且我意識到將會出現大量重複。例如,下面是我想避免的明顯的解決方案:當前對象的訪問類

AbstractVehicle* ConcreteCar::clone() const 
{ 
    auto c = new ConcreteCar; 
    c->setSomething(this->getSomething()); 
    return c; 
} 

AbstractVehicle* ConcreteJet::clone() const 
{ 
    auto j = new ConcreteJet; 
    j->setSomething(this->getSomething()); 
    return j; 
} 

這裏是我想什麼:

AbstractVehicle* AbstractVehicle::clone() const 
{ 
    auto v = new this->ACTUAL_CLASS; 
    v->setSomething(this->getSomething()); 
    return v; 
} 

有沒有什麼簡單的方法來做到這一點?如果不是的話,我很想知道爲什麼會出現這種情況,因爲天真地說,實施起來似乎不像動態調度那樣更具挑戰性。

+3

不可以。您可以使用CRTP來減少克隆所需的樣板,但我不會直接稱它爲簡單。但是我敢肯定你可以找到克隆實現的例子,如果你搜索它們。 –

+0

這似乎是一個矛盾的事情(因爲它是一個抽象接口)。你永遠不應該直接實例化一個'AbstractVehicle'。 – CoffeeandCode

+0

@CoffeeandCode這不是這裏的意圖 – ICoffeeConsumer

回答

1

根據小時數定義您的setSomething/getSomething函數,您可以保留一個克隆,並且可以覆蓋「創建我」類型的函數。例如:

AbstractVehicle* ConcreteCar::MakeNew() { // virtual method 
    return new ConcreteCar(); 
} 

這會從基類的clone被稱爲:

AbstractVehicle* AbstractVehicle::Clone() { 
    auto j = MakeNew(); 
    j->setSomething(this->getSomething()); 
    return j; 
} 
+0

這也是我所得到的,它實際上似乎比我認爲我想要的不存在的功能更靈活,因爲如果需要,我可以調整在由Clone()配置之前,MakeNew()中的對象。我預計對象默認情況下會有這樣的東西,但我猜想,因爲並不是每個類都有默認的構造函數,所以它沒有任何意義。 – ICoffeeConsumer

1

有沒有簡單的方法來做到這一點。但是,(我可能會被絞死這一點)你能避免通過使用預處理器編寫的clone功能的多個定義,定義如下:

#define DEFINE_VEHICLE_CLONE_MEMFUN(Vehicle) \ 
AbstractVehicle* Vehicle::clone() const { \ 
    auto v = new Vehicle;     \ 
    v->setSomething(this->getSomething()); \ 
    return v;        \ 
} 

然後在代碼中定義的成員函數爲:

DEFINE_VEHICLE_CLONE_MEMFUN(ConcreteCar) 
DEFINE_VEHICLE_CLONE_MEMFUN(ConcreteJet) 
2

編號操作符new要求創建的對象的類型在編譯時指定。你所要求的要求在編譯時創建的對象類型是未知的,並且在運行時計算(或以其他方式獲得)。

+0

加1,因爲你回答「我很想知道爲什麼會出現這種情況」 – 101010

0

這可能不是正是你問什麼;但這可能會實現你的目標。我在這裏有一個抽象基類,它有三個定義的派生類。我在虛擬類型的類中沒有克隆方法,但我所擁有的是一個模板函數,可以爲您完成這項工作。

#include <iostream> 
#include <conio.h> 

class Base { 
public: 
    enum Type { 
     TYPE_1 = 0, 
     TYPE_2, 
     TYPE_3, 
    }; // Type 

private: 
    Type m_eType; 

public: 
    virtual ~Base() {} 

    Type getType() const { return m_eType; } 

protected: 
    explicit Base(Type eType) : m_eType(eType) {} 

private: 
    Base(const Base& c); // Not Implemented 
    Base& operator=(const Base& c); // Not Implemented 

}; // Base 

class Derived1 : public Base { 
private: 
    int m_val; 

public: 
    Derived1() : Base(TYPE_1), m_val(0) {} 
    explicit Derived1(int val) : Base(TYPE_1), m_val(val) {} 
    virtual ~Derived1() {} 

    int getValue() const { return m_val; } 
    void setValue(int val) { m_val = val; } 

private: 
    Derived1(const Derived1& c); // Not Implemented 
    Derived1& operator=(const Derived1& c); // Not Implemented 

}; // Derived1 

class Derived2 : public Base { 
private: 
    float m_val; 

public: 
    Derived2() : Base(TYPE_2), m_val(0) {} 
    explicit Derived2(float val) : Base(TYPE_2), m_val(val) {} 
    virtual ~Derived2() {} 

    float getValue() const { return m_val; } 
    void setValue(float val) { m_val = val; } 

private: 
    Derived2(const Derived2& c); // Not Implemented 
    Derived2& operator=(const Derived2& c); // Not Implemented 

}; // Derived2 

class Derived3 : public Base { 
private: 
    double m_val; 

public: 
    Derived3() : Base(TYPE_3), m_val(0) {} 
    explicit Derived3(double val) : Base(TYPE_3), m_val(val) {} 
    virtual ~Derived3() {} 

    double getValue() const { return m_val; } 
    void setValue(double val) { m_val = val; } 

private: 
    Derived3(const Derived3& c); // Not Implemented 
    Derived3& operator=(const Derived3& c); // Not Implemented 

}; // Derived3 

// This Function Template Takes The Base Of The Object And The 
// Derived Type For Its Templated Parameters. The Function's 
// Parameter Takes A Pointer To The Derived Type And A Bool Flag That Is 
// True By Default And Will Make A Complete Clone, If This Is False It Will 
// Make A Fresh Clean Object, It Will Construct 
// A New Object On The Heap, Then Dynamically Cast It To A Pointer Of 
// Its Base And It Will Return Its Base Pointer, Otherwise It Will 
// Return nullptr. NOTE: I Did Not Include Any Error Checking To 
// See If The Two Templated Types Passed In Are Derived From One Another. 
template<class BaseT, class DerivedT> 
BaseT* clone(DerivedT* clonerPtr, bool bClone = true) { 
    BaseT* basePtr = nullptr; 

    if (bClone) { 
     DerivedT* pTemp = new DerivedT(); 
     pTemp = clonerPtr; // Will Require operator=() to be defined for the derived classes. 
     if (pTemp == nullptr) { 
      return nullptr; 
     } 
     basePtr = dynamic_cast<BaseT*>(pTemp); 
    } else { 
     // Create New Empty Object. 
     clonerPtr = new DerivedT(); 
     if (clonerPtr == nullptr) { 
      return nullptr; 
     } 
     basePtr = dynamic_cast<BaseT*>(clonerPtr); 

    }  
    return basePtr; 
} 

int main() { 
    Base* pBase = nullptr; 

    // 1st Case Using The Heap   
    Derived1* ptr1 = new Derived1(5);   
    pBase = clone<Base, Derived2>(ptr1);   
    std::cout << pBase->getType() << std::endl; 

    if (ptr1) { 
     delete ptr1; 
     ptr1 = nullptr; 
    } 
    if (pBase) { 
     delete pBase; 
     pBase = nullptr; 
    } 

    // 2nd Case Using Stack Object 
    pBase = nullptr; // Not need but just incase 
    Derived2 d2(3.41f); 
    pBase = clone<Base, Derived2>(&d2); 
    std::cout << pBase->getType() << std::endl; 

    if (pBase) { 
     delete pBase; 
     pBase = nullptr; 
    } 

    std::cout << "Press any key to quit." << std::endl; 
    _getch(); 
    return 0; 
} 

另一個提醒是,模板函數和類都不會通過new和delete操作符清除堆中的內存。所以需要謹慎使用這種方法或方法。如果想要實現類似的方法,但不必擔心清理動態內存,那麼應該在函數模板中使用智能指針。