[英]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.