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

时间:2015-07-14 01:02:13

标签: 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();
    }
}

感谢。

3 个答案:

答案 0 :(得分:5)

考虑汽车的情况。

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

你可以将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只生Bs,不需要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?!?";
    }
};

这是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仍然绝对没有用或参与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;
}

答案 1 :(得分: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();
        }

答案 2 :(得分:0)

您可能想要定义一个以基类实例作为参数的构造函数,以便以后可以使用static_cast从基类转换为派生类。

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

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

如果要使用基类实例创建派生类实例,则两者之间有一些转换规则,可以使用派生类构造函数明确指定。