使用基类实例创建派生类实例

Nee*_*t33 5 c++ oop inheritance

我有一个基类实例,有一个从基类继承的派生类,我想将基实例转换成派生实例,(如果可能的话,不复制任何内容(可能会向派生类发送基类的引用)) )如何实现?

注意:之所以需要它,是因为我使用的是工厂设计模式,该模式标识需要使用位于基本实例中的参数创建的派生类。

//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();
    }
}
Run Code Online (Sandbox Code Playgroud)

谢谢。

use*_*301 5

考虑一下汽车的情况。

您可以将兰博基尼当作汽车。

您可以将Yugo当作汽车。

如果汽车是兰博基尼,则可以将其视为兰博基尼。在C ++中,这意味着真正指向兰博基尼的汽车指针。为了使兰博基尼指针从汽车指针中移出,您应该使用dynamic_cast。如果汽车未指向兰博基尼,则dynamic_cast将返回NULL。这样一来,您就可以避免假装兰博基尼(Lamborghini)成为优戈(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();
    }
}
Run Code Online (Sandbox Code Playgroud)

将C复制到B,然后将B返回。B需要一个采用C的构造函数,但要点很重要。如果B是纯虚拟的,则无法实例化。现在,我们将忽略将new C()

同样,这个问题也不能使用引用,因此您陷入了返回指针的困境

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

现在,我将提出一个建议:将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;
    }
};
Run Code Online (Sandbox Code Playgroud)

但是,这引出了另一个建议:B为什么要知道什么?在这个水平上,A的意义是什么?为什么A将类的构建代码存储到层次结构的两个或更多个步骤中?从维护的角度来看很糟糕。对象的重点是他们知道自己是谁以及如何操纵自己。短路会导致疼痛。

class B: public A
{
public:
    virtual ~B(){}
    virtual B* makeB() = 0;
};
Run Code Online (Sandbox Code Playgroud)

现在,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();
    }
};
Run Code Online (Sandbox Code Playgroud)

编辑:传统工厂

您要的是抽象工厂。为此,您不需要任何东西。您甚至不需要上课。您当然不需要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_ */
Run Code Online (Sandbox Code Playgroud)

在这里,我将稍微偏离本书。我将使用枚举,而不是使用整数来标识要构建的类型。原因有两个:比1更易于阅读和理解gimme_a_C,并且如果尝试提供未枚举的值,则会生成编译器错误。

enum bType
{
    gimmie_a_C,
    gimmie_a_D,
    gimmie_an_E
};
Run Code Online (Sandbox Code Playgroud)

如果将枚举更新为新类型(gimmie_an_E),但工厂未更新,则标记愚蠢的异常。

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

这是Factory客户需要查看的所有内容。他们看不到C。看不到D。除了C中列出的名称之外,他们不知道C和D以任何方式存在enum bType。他们所看到的只是指向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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我让读者自己去发现上面代码中的愚蠢错误,这些错误使它们易于出错,以及为什么我不喜欢它们。

用法,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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

和新的主要:

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;
}
Run Code Online (Sandbox Code Playgroud)