简体   繁体   English

使用基础 class 实例创建派生 class 实例

[英]Creating derived class instance using base class instance

I have a base class instance, there is a derived class that inherits from the base class, I want to transform the base instance into derived instance, (if possible without copying anything (maybe sending to the derived class a reference of the base class)) how can I achieve that? I have a base class instance, there is a derived class that inherits from the base class, I want to transform the base instance into derived instance, (if possible without copying anything (maybe sending to the derived class a reference of the base class) ) 我怎样才能做到这一点?

Note: I need this because I'm using factory design pattern which identify the derived class needed to be created using a parameter located in the base instance.注意:我需要这个,因为我正在使用工厂设计模式,该模式识别需要使用位于基本实例中的参数创建的派生 class。

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

Thanks.谢谢。

Consider the case of the car. 考虑一下汽车的情况。

You can treat a Lamborghini as a car. 您可以将兰博基尼当作汽车。

You can treat a Yugo as a car. 您可以将Yugo当作汽车。

You can treat a car as a Lamborghini if it is a Lamborghini. 如果汽车是兰博基尼,则可以将其视为兰博基尼。 In C++ this means a pointer to car that really points to a Lamborghini. 在C ++中,这意味着真正指向兰博基尼的汽车指针。 In order to get a Lamborghini pointer back out of the car pointer you should use dynamic_cast. 为了使兰博基尼指针从汽车指针中移出,您应该使用dynamic_cast。 If the car does not point to a Lamborghini, dynamic_cast will return NULL. 如果汽车未指向兰博基尼,则dynamic_cast将返回NULL。 This keeps you from trying to pass off a Yugo as a Lamborghini and blowing the Yugo's engine. 这样一来,您就不会像兰博基尼那样冒充Yugo并吹牛。

But when the Lamborghini is being treated as a car, it can only do car things. 但是,当兰博基尼被当作汽车时,它只能做汽车。 If you copy a Lamborghini into a car, you strip out all Lamborghini-ness forever. 如果将兰博基尼复制到汽车中,则会永远剥夺所有兰博基尼性。 It's gone. 没了。

Code time! 编码时间!

This, I'm afraid cannot be done: 恐怕无法做到这一点:

//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 is being copied into a B and the B is being returned. 将C复制到B,然后将B返回。 B would need a constructor that took a C, but the point is moot. B需要一个采用C的构造函数,但要点很重要。 B cannot be instantiated if it's pure virtual. 如果B是纯虚拟的,则无法实例化。 For now we'll ignore the leak that would be new C() 现在,我们将忽略将是new C()的泄漏

Also can't use a reference for this job, pretty much the same problem, so you're trapped into returning a pointer 同样,这个问题也不能使用引用,因此您被困在返回一个指针中

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

Now I'm going to make a suggestion: Build the make function into B and handle the case where A doesn't map to anything recognized by B. 现在,我将提出一个建议:将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;
    }
};

But this leads to another recommendation: Why should B know anything? 但是,这引出了另一个建议:B为什么要知道什么? And What is the point of A at this level? 在这个水平上,A的意义是什么? Why is A storing build codes for classes two or more steps down the hierarchy? 为什么A将类的构建代码存储到层次结构的两个或更多个步骤中? Bad from a maintenance point of view. 从维护的角度来看很糟糕。 The point of objects is they know who they are and how to manipulate themselves. 对象的重点是他们知道自己是谁以及如何操纵自己。 Short-circuiting this leads to pain. 短路会导致疼痛。

class B: public A
{
public:
    virtual ~B(){}
    virtual B* makeB() = 0;
};

Now B only makes Bs, needs no help from A, and those who extend B are stuck with figuring out how to make themselves--a task they should know better than anyone else. 现在,B只做B,不需要A的帮助,而那些扩展B的人都想出了如何使自己变得更容易的东西,他们应该比其他任何人都要了解这一任务。 Much safer because there is never any possibility of a code unrecognised by B for a new class. 安全得多,因为对于新类,B永远不会存在无法被B识别的代码的可能性。

class C: public B
{
public:
    B* makeB()
    {
        return new C();
    }
};

class D: public B
{
public:
    B* makeB()
    {
        return new D();
    }
};

Edit: Traditional factory 编辑:传统工厂

You're asking for an abstract factory. 您要一个抽象工厂。 For that you need nothing. 为此,您不需要任何东西。 You don't even need a class. 您甚至不需要上课。 You certainly don't need a class A. The goal of this sort of factory is the caller knows nothing about the class. 您当然不需要A类。此类工厂的目标是调用方对该类一无所知。 By providing an A, the caller needs to know how to make an A or have another factory that makes an A. 通过提供A,呼叫者需要知道如何制造A或拥有另一个制造A的工厂。

First a bit of set-up in a header file BFactory.h: 首先在头文件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_ */

Here I'm going to deviate from the book way a little. 在这里,我将稍微偏离本书。 Rather than using an integer to identify the type to be built, I'm going to use an enum. 我将使用枚举,而不是使用整数来标识要构建的类型。 Two reasons: Easier to read and understand gimme_a_C than 1 and generates a compiler error if you try to provide a value that is not enumerated. 原因有两个:比1更易于阅读和理解gimme_a_C,并且如果尝试提供未枚举的值,则会生成编译器错误。

enum bType
{
    gimmie_a_C,
    gimmie_a_D,
    gimmie_an_E
};

And an exception to flag stupidity if the enum is updated with new types (gimmie_an_E) but the factory is not. 如果将枚举更新为新类型(gimmie_an_E),但工厂未更新,则标记愚蠢的异常。

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

This is all the Factory client needs to see. 这是Factory客户需要查看的所有内容。 They don't see C. They don't see D. They have no clue that C and D exist in any way other than the names listed in enum bType . 他们看不到C。看不到D。除了enum bType列出的名称之外,他们不知道C和D以任何方式存在。 All they ever see is pointers to B. 他们所看到的只是指向B的指针。

Now for the implementation BFactory.cpp: 现在执行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();
    }
}

I'll leave it up to the reader to spot the stupid bug in the above code that makes these error prone and why I don't like them. 我让读者自己去发现上面代码中的愚蠢错误,这些错误使它们易于出错,以及为什么我不喜欢它们。

And usage, main.cpp: 用法,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;
    }
}

There is still absolutely no use for or involvement of A. A if it exists, should no nothing about B or the children of B. A绝对没有任何用处或参与。A(如果存在)对B或B的子代一无所知。

These days there is a little improvement we can make so that the pointers are a little safer. 这些天,我们可以做一些改进,以便使指针更安全。 unique_ptr allows us to maintain the polymporphic advantages of a pointer to B without the memory management woes. 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();
    }
}

and the new 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;
}

Although it is impossible to alter the type of an object you still can make instances of base and derived classes share the same data: 尽管无法更改对象的类型,但仍然可以使基类和派生类的实例共享相同的数据:

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

You might want to define a constructor that takes the base class instance as the argument so you can later use static_cast to convert from the base class to the derived class. 您可能想要定义一个以基类实例作为参数的构造函数,以便以后可以使用static_cast从基类转换为派生类。

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

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

If you want to create a derived class instance using the base class instance then there is some conversion rule between the two, which you can specify explicitly using a derived class constructor. 如果要使用基类实例创建派生类实例,则两者之间有一些转换规则,您可以使用派生类构造函数来明确指定。

A base class should not "know" about how to make its own derived class instances.基础 class 不应该“知道”如何制作自己的派生 class 实例。 That is the point of inheritance.这就是 inheritance 的要点。

The "is a" relationship of derived classes means that any subclass instance will pass as a base class instance transparently, and you can treat it as one, and by default base class non-virtual methods are called on a base class reference, even if it a derived class instance.派生类的“是一个”关系意味着任何子类实例都将作为基础 class 实例透明地传递,您可以将其视为一个,默认情况下,基础 class 非虚拟方法在基础 ZA2F2ED4F8EBC0661C21A29 引用上调用它是派生的 class 实例。 Only virtual methods use the derived class method.只有虚拟方法使用派生的 class 方法。

In the case of creating a base class instance from a derived class you want to "slice" the instance data (normally a bad thing and normally a mistake).在从派生的 class 创建基本 class 实例的情况下,您想要“切片”实例数据(通常是坏事,通常是错误)。

class A{ // ... A stuff };

class B : A
{   //  ... B stuff
  A make_A() {  return (A) B(*this); } // copy cast to A
};

Under no circumstances try to do this:在任何情况下都不要尝试这样做:

class B;
class A { // ...
   B make_B() { return B(*this); }
};

That is inverted OO logic.那是倒置的OO逻辑。 It requires at least 2 scans of the source code, which C++ does not do.它至少需要对源代码进行 2 次扫描,而 C++ 不这样做。 It fails.它失败。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM