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

新手上路,请多包涵

我有一个基类实例,有一个从基类继承的派生类,我想将基类实例转换为派生实例,(如果可能的话不复制任何东西(可能向派生类发送基类的引用) ) 我怎样才能做到这一点?

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

 //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();
    }
}

谢谢。

原文由 Neet33 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 371
2 个回答

考虑汽车的情况。

您可以将兰博基尼视为汽车。

您可以将 Yugo 视为汽车。

如果它是兰博基尼,您可以将汽车视为兰博基尼。在 C++ 中,这意味着指向真正指向兰博基尼的汽车的指针。为了从汽车指针中取出兰博基尼指针,您应该使用 dynamic_cast。如果汽车不指向兰博基尼,dynamic_cast 将返回 NULL。这可以防止您试图将 Yugo 伪装成兰博基尼并炸毁 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 永远不会为新类识别代码。

 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_ */

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

 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?!?";
    }
};

这是工厂客户需要看到的所有内容。他们看不到 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;
}

原文由 user4581301 发布,翻译遵循 CC BY-SA 3.0 许可协议

尽管不可能改变对象的类型,但您仍然可以使基类和派生类的实例共享相同的数据:

         #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();
        }

原文由 alexm 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题