首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >使用基类实例创建派生类实例

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

Stack Overflow用户
提问于 2015-07-14 09:02:13
回答 3查看 11.6K关注 0票数 6

我有一个基类实例,有一个继承自基类的派生类,我想将基类实例转换为派生实例,(如果可能的话,不复制任何东西(可能是向派生类发送基类的引用))如何实现?

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

代码语言:javascript
复制
//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();
    }
}

谢谢。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-07-14 11:32:40

以汽车为例。

你可以把一辆兰博基尼当做一辆车。

你可以把Yugo当做一辆车。

如果一辆车是兰博基尼,你可以把它当做兰博基尼。在C++中,这意味着指向真正指向兰博基尼的汽车的指针。为了将兰博基尼指针从汽车指针中取出来,你应该使用dynamic_cast。如果汽车没有指向兰博基尼,dynamic_cast将返回NULL。这使你不会试图冒充一辆Yugo作为兰博基尼,并炸毁Yugo的引擎。

但当兰博基尼被视为一辆汽车时,它只能做汽车的事情。如果你把一辆兰博基尼复制到汽车上,你就永远失去了所有兰博基尼的特色。它不见了。

代码时间到了!

这一点,恐怕不能做到:

代码语言:javascript
复制
//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()的泄漏

也不能为这个任务使用引用,几乎同样的问题,所以你被困在返回一个指针中

代码语言:javascript
复制
B * BFactory::makeB(A &a) {
    int n=a.getN();
    if(n==1){
        return new C();
    }
}

现在我将提出一个建议:将make函数构建到B中,并处理A未映射到B可识别的任何内容的情况。

代码语言:javascript
复制
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存储类的构建代码在层次结构中向下两步或更多?从维护的角度来看是不好的。对象的意义在于它们知道自己是谁,以及如何操纵自己。短路会导致疼痛。

代码语言:javascript
复制
class B: public A
{
public:
    virtual ~B(){}
    virtual B* makeB() = 0;
};

现在B只生成B,不需要A的帮助,而那些扩展B的人被困在如何让自己变得更好--这是他们应该比任何人都更清楚的任务。更安全,因为对于一个新的类,永远不会有B不能识别的代码。

代码语言:javascript
复制
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中进行一些设置:

代码语言:javascript
复制
#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更容易阅读和理解,如果您试图提供一个未枚举的值,则会生成一个编译器错误。

代码语言:javascript
复制
enum bType
{
    gimmie_a_C,
    gimmie_a_D,
    gimmie_an_E
};

如果枚举使用新类型(gimmie_an_E)更新,但工厂没有更新,则标记愚蠢是一个例外。

代码语言:javascript
复制
class BadTypeException: public std::exception
{
public:
    const char* what() const noexcept
    {
        return "Dude! WTF?!?";
    }
};

这就是Factory客户端需要看到的全部内容。他们看不到C。他们看不到D。他们不知道C和D以任何方式存在,除了enum bType中列出的名称。他们所看到的都是指向B的指针。

现在来看一下实现BFactory.cpp:

代码语言:javascript
复制
#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();
    }
}

我将留给读者去发现上面代码中愚蠢的bug,它使这些错误变得容易发生,以及为什么我不喜欢它们。

和用法,main.cpp:

代码语言:javascript
复制
#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的指针的多义性优势,而不会有内存管理的麻烦。

代码语言:javascript
复制
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();
    }
}

和新的main:

代码语言:javascript
复制
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;
}
票数 6
EN

Stack Overflow用户

发布于 2019-05-29 07:45:48

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

代码语言:javascript
复制
class Derived : public Base
{
public:
  Derived(const Base& base) : Base{base} {}
};

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

如果您想使用基类实例创建一个派生类实例,那么在两者之间有一些转换规则,您可以使用派生类构造函数显式指定。

票数 2
EN

Stack Overflow用户

发布于 2015-07-14 09:44:26

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

代码语言:javascript
复制
        #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();
        }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31395957

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档