簡體   English   中英

使用基礎 class 實例創建派生 class 實例

[英]Creating derived class instance using base class instance

I have a base class instance, there is a derived class that inherits from the base class, I want to transform the base instance into derived instance, (if possible without copying anything (maybe sending to the derived class a reference of the base class) ) 我怎樣才能做到這一點?

注意:我需要這個,因為我正在使用工廠設計模式,該模式識別需要使用位於基本實例中的參數創建的派生 class。

//class A
//class B: public A (pure virtual)
//class C: public B

B BFactory::makeB(A &a) {
    int n=a.getN();
    if(n==1){
        return new C();
    }
}

謝謝。

考慮一下汽車的情況。

您可以將蘭博基尼當作汽車。

您可以將Yugo當作汽車。

如果汽車是蘭博基尼,則可以將其視為蘭博基尼。 在C ++中,這意味着真正指向蘭博基尼的汽車指針。 為了使蘭博基尼指針從汽車指針中移出,您應該使用dynamic_cast。 如果汽車未指向蘭博基尼,則dynamic_cast將返回NULL。 這樣一來,您就不會像蘭博基尼那樣冒充Yugo並吹牛。

但是,當蘭博基尼被當作汽車時,它只能做汽車。 如果將蘭博基尼復制到汽車中,則會永遠剝奪所有蘭博基尼性。 沒了。

編碼時間!

恐怕無法做到這一點:

//class A
//class B: public A (pure virtual)
//class C: public B

B BFactory::makeB(A &a) {
    int n=a.getN();
    if(n==1){
        return new C();
    }
}

將C復制到B,然后將B返回。 B需要一個采用C的構造函數,但要點很重要。 如果B是純虛擬的,則無法實例化。 現在,我們將忽略將是new C()的泄漏

同樣,這個問題也不能使用引用,因此您被困在返回一個指針中

B * BFactory::makeB(A &a) {
    int n=a.getN();
    if(n==1){
        return new C();
    }
}

現在,我將提出一個建議:將make函數構建到B中,並處理A不會映射到B所識別的任何內容的情況。

class B: public A
{
public:
    virtual ~B(){}
    static B * makeB(A & a)
    {
        switch(a.getN())
        {
            case 1:
                return new C();
        }
        return NULL;
    }
};

但是,這引出了另一個建議:B為什么要知道什么? 在這個水平上,A的意義是什么? 為什么A將類的構建代碼存儲到層次結構的兩個或更多個步驟中? 從維護的角度來看很糟糕。 對象的重點是他們知道自己是誰以及如何操縱自己。 短路會導致疼痛。

class B: public A
{
public:
    virtual ~B(){}
    virtual B* makeB() = 0;
};

現在,B只做B,不需要A的幫助,而那些擴展B的人都想出了如何使自己變得更容易的東西,他們應該比其他任何人都要了解這一任務。 安全得多,因為對於新類,B永遠不會存在無法被B識別的代碼的可能性。

class C: public B
{
public:
    B* makeB()
    {
        return new C();
    }
};

class D: public B
{
public:
    B* makeB()
    {
        return new D();
    }
};

編輯:傳統工廠

您要一個抽象工廠。 為此,您不需要任何東西。 您甚至不需要上課。 您當然不需要A類。此類工廠的目標是調用方對該類一無所知。 通過提供A,呼叫者需要知道如何制造A或擁有另一個制造A的工廠。

首先在頭文件BFactory.h中進行設置:

#ifndef BFACTORY_H_
#define BFACTORY_H_

#include <exception>
class B
{
public:
    virtual ~B(){}
    virtual std::string whatAmI() = 0;
protected:
    // data members common to all B subclasses
};

enum bType
{
    gimmie_a_C,
    gimmie_a_D,
    gimmie_an_E
};

class BadTypeException: public std::exception
{
public:
    const char* what() const noexcept
    {
        return "Dude! WTF?!?";
    }
};

B* BFactory(enum bType type);

#endif /* BFACTORY_H_ */

在這里,我將稍微偏離本書。 我將使用枚舉,而不是使用整數來標識要構建的類型。 原因有兩個:比1更易於閱讀和理解gimme_a_C,並且如果嘗試提供未枚舉的值,則會生成編譯器錯誤。

enum bType
{
    gimmie_a_C,
    gimmie_a_D,
    gimmie_an_E
};

如果將枚舉更新為新類型(gimmie_an_E),但工廠未更新,則標記愚蠢的異常。

class BadTypeException: public std::exception
{
public:
    const char* what() const noexcept
    {
        return "Dude! WTF?!?";
    }
};

這是Factory客戶需要查看的所有內容。 他們看不到C。看不到D。除了enum bType列出的名稱之外,他們不知道C和D以任何方式存在。 他們所看到的只是指向B的指針。

現在執行BFactory.cpp:

#include "BFactory.h"

class C:public B
{
    std::string whatAmI()
    {
        return "C";
    }
};

class D:public B
{
    std::string whatAmI()
    {
        return "D";
    }
};

B* BFactory(enum bType type)
{
    switch(type)
    {
        case gimmie_a_C:
            return new C();
        case gimmie_a_D:
            return new C();
        default:
            throw BadTypeException();
    }
}

我讓讀者自己去發現上面代碼中的愚蠢錯誤,這些錯誤使它們易於出錯,以及為什么我不喜歡它們。

用法,main.cpp:

#include "BFactory.h"

int main()
{
    B * temp;
    temp = BFactory(gimmie_a_C);
    std::cout << temp->whatAmI() << std::endl;
    delete temp;
    temp = BFactory(gimmie_a_D);
    std::cout << temp->whatAmI() << std::endl;
    delete temp;
    //temp = BFactory(1001); // won't compile
    try
    {
        temp = BFactory(gimmie_an_E); // will compile, throws exception 
        std::cout << temp->whatAmI() << std::endl;
    }
    catch(BadTypeException& wtf)
    {
        std::cerr << wtf.what() << std::endl;
    }
}

A絕對沒有任何用處或參與。A(如果存在)對B或B的子代一無所知。

這些天,我們可以做一些改進,以便使指針更安全。 unique_ptr使我們能夠保持指向B的指針的多態性優勢,而不會造成內存管理問題。

std::unique_ptr<B> BFactory(enum bType type)
{
    switch(type)
    {
        case gimmie_a_C:
            return std::unique_ptr<B>(new C());
        case gimmie_a_D:
            return std::unique_ptr<B>(new D());
        default:
            throw BadTypeException();
    }
}

和新的主要:

int main()
{
    std::unique_ptr<B> temp;
    temp = BFactory(gimmie_a_C);
    std::cout << temp->whatAmI() << std::endl;
    temp = BFactory(gimmie_a_D);
    std::cout << temp->whatAmI() << std::endl;
}

盡管無法更改對象的類型,但仍然可以使基類和派生類的實例共享相同的數據:

        #include <memory>
        #include <iostream>

        class Base
        {
        protected:

            struct CommonData
            {
                int A;
                int B;
            };

            std::shared_ptr<CommonData> m_data;



        public:

            Base() : m_data(std::make_shared<CommonData>())
            {
                m_data->A = 0;
                m_data->B = 0;
            }

            void SetData(Base * source)
            {
                m_data = source->m_data;
            }


            int A() const { return m_data->A; }
            int B() const { return m_data->B; }

            void SetA(int value) { m_data->A = value; }
            void SetB(int value) { m_data->B = value; }
        };

        class Derived : public Base
        {
        public:
            int C;
        };

        using namespace std;

        int _tmain(int argc, _TCHAR* argv[])
        {

            Base base;
            base.SetA(12);
            base.SetB(46);

            Derived derived;
            derived.SetData(&base);
            derived.C = 555;

            cout << derived.A() << endl; // 12         
            cout << derived.C << endl; // 555;

            cin.get();
        }

您可能想要定義一個以基類實例作為參數的構造函數,以便以后可以使用static_cast從基類轉換為派生類。

class Derived : public Base
{
public:
  Derived(const Base& base) : Base{base} {}
};

int main()
{
  Base a;
  Derived b = static_cast<Derived>(a);
}

如果要使用基類實例創建派生類實例,則兩者之間有一些轉換規則,您可以使用派生類構造函數來明確指定。

基礎 class 不應該“知道”如何制作自己的派生 class 實例。 這就是 inheritance 的要點。

派生類的“是一個”關系意味着任何子類實例都將作為基礎 class 實例透明地傳遞,您可以將其視為一個,默認情況下,基礎 class 非虛擬方法在基礎 ZA2F2ED4F8EBC0661C21A29 引用上調用它是派生的 class 實例。 只有虛擬方法使用派生的 class 方法。

在從派生的 class 創建基本 class 實例的情況下,您想要“切片”實例數據(通常是壞事,通常是錯誤)。

class A{ // ... A stuff };

class B : A
{   //  ... B stuff
  A make_A() {  return (A) B(*this); } // copy cast to A
};

在任何情況下都不要嘗試這樣做:

class B;
class A { // ...
   B make_B() { return B(*this); }
};

那是倒置的OO邏輯。 它至少需要對源代碼進行 2 次掃描,而 C++ 不這樣做。 它失敗。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM