因此,在理解应该如何解决遇到的这种多态性问题时,我遇到了一些问题。为了简短起见,让我们仅定义两个级别的类,一个父级和两个儿子:

parent :

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 =应该足够了,在实际项目中,这些类更为复杂,因此我确实需要自己实现一个,并且它们成功地调用了 parent 的。

所以现在,在我项目的某个地方,我有一个Entity *数组,它只能是son1或son2。我想遍历这个实体数组并将副本复制到另一个数组中。我希望能够编写的代码如下:
for (i = 0; i < entities; ++i) {
    entityCopiesArray[i] = entitiesArray[i];
}

问题是,entityCopiesArray和entitysArray均为(Entity *)类型,因此当分配完成时,仅Entity.operator =()被调用,而在当前实体为son1的情况下,我需要同时拥有Entity.operator = ()和EntitySon1.operator =()调用。

我知道我可以将每个变量都强制转换为数组,以便调用正确的运算符,但是这需要额外的信息来说明哪些实体是son1,哪些实体是son2,并且还需要为每个可能的实体的son手动键入。

还有其他解决方法吗,还是我坚持这样做:
if (isSon1())
    (EntitySon1)entityCopiesArray[i] = (EntitySon1)entitiesArray[i];
else if (isSon2())
   (EntitySon2)entityCopiesArray[i] = (EntitySon2)entitiesArray[i];
// ... etc

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

最佳答案

考虑以下:

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)
{
}

我对cloneDerived1实现
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,并使用当前对象作为rhs调用新的空克隆的copy-ctor并填充该克隆。

关于c++ - C++对象多态性问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20295853/

10-15 17:56