C ++对象多态性问题

时间:2013-11-30 02:34:57

标签: c++ inheritance polymorphism

所以我在理解如何解决我遇到的这种多态问题时遇到了一些问题。简而言之,我们只定义两个级别的类,父亲和两个儿子:

父母:

class Entity {
public:
    int x;

    Entity();
    ~Entity();
    Entity(const Entity&);
    Entity &operator=(const Entity&);
};

两个儿子:

class EntitySon1 : public Entity {
public:
    int b;

    EntitySon1();
    ~EntitySon1();
    EntitySon1(const EntitySon1&);
    EntitySon1 &operator=(const EntitySon1&);
};

class EntitySon2 : public Entity {
public:
    int a;

    EntitySon2();
    ~EntitySon2();
    EntitySon2(const EntitySon2&);
    EntitySon2 &operator=(const EntitySon2&);
};

请忘记,在这个例子中,所有类只有一个int值,因此标准operator =应该足够了,在实际项目中这些类更复杂,所以我确实需要自己实现一个和它们也成功地称为父母。

所以现在,在我的项目的某个地方,我有一个Entity *数组,它只能是son1或son2。我想通过这个实体数组并将副本复制到另一个数组中。我希望能够编写的代码如下:

for (i = 0; i < entities; ++i) {
    entityCopiesArray[i] = entitiesArray[i];
}

问题是,entityCopiesArray和entitiesArray都是类型(Entity *)所以当完成赋值时,只有Entity.operator =()被调用,而我需要,在当前实体是son1的情况下,有两个实体.operator =()和EntitySon1.operator =()调用。

我知道我可以在数组上转换每个变量,以便调用正确的运算符,但这需要额外的信息来判断哪些实体是son1,哪些是son2,还需要为每个可能的实体的子进行手动输入。 / p>

还有其他方法,或者我坚持做:

if (isSon1())
    (EntitySon1)entityCopiesArray[i] = (EntitySon1)entitiesArray[i];
else if (isSon2())
   (EntitySon2)entityCopiesArray[i] = (EntitySon2)entitiesArray[i];
// ... etc

编辑:我在深夜做了这篇文章并试图尽可能地压缩信息所以我说的是不准确的事情。我显然有一个实体*的数组,否则我将无法使用多态。

2 个答案:

答案 0 :(得分:2)

  

我有一组实体,它们只能是son1或son2。

一般情况下,这是不可能的。但是,您可以拥有一个Entity*数组,每个数组都指向Son1Son2。然后,您可以拥有一个虚拟clone函数,以便该系列的成员创建自身的副本并向其返回指针(类型为Entity*)。然后你可以很容易地复制数组。

答案 1 :(得分:2)

请考虑以下事项:

struct Base {
    int a;
};

struct Derived1 : public Base {
    int d1Data[100];
};

struct Derived2 : public Base {
    char d2Data[1500];
};

现在,如果我们执行以下操作:

Entity* e = new Entity;
Derived1* d1 = new Derived1;
Derived2* d2 = new Derived2;

std::cout << sizeof(*e) << ", " << sizeof(*d1) << ", " << sizeof(*d2) << '\n';

输出是什么?提示:数字不一样。

那么现在在以下每种情况下会发生什么?

*e = *(Entity*)d1;
*(Derived1*)e = *d1;
*(Derived2*)d1 = *d2;
*(Entity*)d1 = *(Entity*)(d2);
*(Derived1*)d2 = *d1;

这些案例都不是特别好。您的帖子听起来好像是在为object slicing的错误案例做好准备。

不要做。

另一方面,如果您要做的是从列表中克隆对象:

std::vector<Base*> entities;
std::vector<Base*> copies;

entities.push_back(new Derived1);
entities.push_Back(new Derived2);

for (size_t i = 0; i < entities.size(); ++i) {
    Base* clone = make_a_copy_of(entities[i]);
}

然后执行此操作的方法是向Base添加成员函数。

struct Base {
    int a;
    virtual Base* clone() const = 0;
};

struct Derived1 : public Base {
    int d1Data[100];
    Base* clone() const override {
        return new Derived1(*this);
    }
};

struct Derived2 : public Base {
    char d2Data[1500];
    Base* clone() const override {
        return new Derived2(*this);
    }
};

int main() {
    std::vector<Base*> entities;
    std::vector<Base*> copies;

    entities.push_back(new Derived1);
    entities.push_Back(new Derived2);

    for (size_t i = 0; i < entities.size(); ++i) {
        Base* clone = entities[i]->clone();
    }

    // remember to delete all the objects we allocated,
    // or wrap them with std::unique_ptr

    return 0;
}

我可能会因为使用像这样的原始指针而不使用像std :: unique_ptr之类的东西来确保对象具有生命周期而感到愤怒,所以这里是使用unique_ptr的完整版本。我没有使用make_unique,因为我的GCC(4.8.2)或MSVC似乎都不支持它。

#include <iostream>
#include <vector>
#include <memory>

struct Base {
    int m_a;
    Base(int a) : m_a(a) {}
    virtual ~Base() { std::cout << "Dtoring " << m_a << '\n'; }

    virtual std::unique_ptr<Base> clone() const = 0;
};

struct Derived1 : public Base {
    int d1Data[100];

    Derived1(int a) : Base(a) {}
    virtual ~Derived1() { std::cout << "D1 at " << (void*)this << " dtord\n"; }

    std::unique_ptr<Base> clone() const override { return std::unique_ptr<Derived1>(new Derived1 (*this)); }
};

struct Derived2 : public Base {
    char d2Data[10000];

    Derived2(int a) : Base(a) {}
    virtual ~Derived2() { std::cout << "D1 at " << (void*)this << " dtord\n"; }

    std::unique_ptr<Base> clone() const override { return std::unique_ptr<Derived2>(new Derived2 (*this)); }
};

int main()
{
    std::vector<std::unique_ptr<Base>> entities;
    {
        std::vector<std::unique_ptr<Base>> copies;

        entities.emplace_back(new Derived1 (3));
        entities.emplace_back(new Derived2 (5));

        for (auto& ent : entities) {
            copies.emplace_back(ent->clone());
        }

        std::cout << "copies going out of scope\n";
    }

    std::cout << "entities going out of scope\n";

    return 0;
}

现场演示:http://ideone.com/lrgJun

----编辑----

继承类时,还会将其数据成员继承到整个结构中。在此示例中,Derived1的有效结构为:

struct Derived1 {
    int a; // from Base
    int d1Data[100];
};

clone的实现正在悄悄地依赖于复制构造函数,它实质上是将src的内存副本转换为dest,相当于memcpy(this, src, sizeof(*this));。因此,您不需要将调用链接到clone或类似的东西,魔术就在复制构造函数中完成。

如果您需要将Base的实例添加到您的混音中,我们可以在clone中实施Base成员 - 但请记住,任何“特殊”您添加到Base的成员也将在所有派生类中继承。

我会稍微对这些类进行卷积,并向您展示Base和Derived1的副本构造函数有效的样子:

struct Base {
    int m_int;
    double m_double;
    std::string m_name;
private:
    unsigned int m_baseOnly;
    ...
};

struct Derived1 : public Base {
    // inherited m_int, m_double and m_name
    // also inherited m_baseOnly, we just can't access it.
    std::array<int, 100> m_data;
    std::string m_title;
    std::shared_ptr<Base> m_buddy;
    ...
};

此时,Derived1的实际内存结构如下所示:

Derived1 {
    int m_int;
    double m_double;
    std::string m_name;
    unsigned int m_baseOnly;
    std::array<int, 100> m_data;
    std::string m_title;
    std::shared_ptr<Base> m_buddy;
};

鉴于这些定义,除非我们实现自己的复制构造函数或禁用复制构造,否则这实际上是编译器将为我们生成的:

Base::Base(const Base& rhs) // Base copy constructor
    : m_int(rhs.m_int)
    , m_double(rhs.m_double)
    , m_name(rhs.m_name)
    , m_baseOnly(rhs.m_baseOnly)
{
}

Derived1::Derived1(const Derived1& rhs)
    : Base(rhs) // copy-construct the Base portion
    , m_data(rhs.m_data) // hence why I used std::array
    , m_title(rhs.m_title)
    , m_buddy(rhs.m_buddy)
{
}

我对clone

Derived1实施
std::unique_ptr<Base> clone() const override
{
    return std::unique_ptr<Derived1>(new Derived1 (*this));
}

std::unique_ptr<Base> clone() const override
{
    const Derived1& rhs = *this; // Reference to current object.
    Derived1* newClone = new Derived1(rhs);
    return std::unique_ptr<Derived1>(newClone);
}

创建一个新的Derived1,调用new,empty,clone的copy-ctor,当前对象为rhs并填写克隆。