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

Creating derived class instance using base class instance

本文关键字:实例 派生 创建 基类      更新时间:2023-10-16

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

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

//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++中,这意味着指向car的指针实际上指向兰博基尼。为了将Lamborghini指针从汽车指针中取出,你应该使用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_ */

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

enum bType
{
    gimmie_a_C,
    gimmie_a_D,
    gimmie_an_E
};

如果枚举被更新为新类型(gimmi_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();
    }
}

和新的main:

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;
}

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

class Derived : public Base
{
public:
  Derived(const Base& base) : Base{base} {}
};
int main()
{
  Base a;
  Derived b = static_cast<Derived>(a);
}

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

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

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

基类不应该"知道";关于如何创建自己的派生类实例。这就是继承的意义。

这个"是"派生类的关系意味着任何子类实例将透明地作为基类实例传递,并且您可以将其视为基类实例,并且默认情况下基类非虚方法在基类引用上被调用,即使它是派生类实例。只有虚方法使用派生类方法。

在从派生类创建基类实例的情况下,您希望"切片"。实例数据(通常是坏事,通常是错误的)。

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逻辑。它需要对源代码进行至少两次扫描,而c++不需要这样做。它失败。