所以我在理解如何解决我遇到的这个多态问题时遇到了一些问题。简而言之,让我们只定义两个级别的类,一个父亲和两个儿子:
父级:
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 值,因此标准运算符=应该就足够了,在实际项目中,这些类更复杂,所以我确实需要自己实现一个,并且它们也成功地调用了父类。
所以现在,在我的项目中的某个地方,我有一个实体*数组,它只能是 son1 或 son2。我想遍历这个实体数组并将副本复制到另一个数组中。我希望能够编写的代码是这样的:
for (i = 0; i < entities; ++i) {
entityCopiesArray[i] = entitiesArray[i];
}
问题是,entityCopyiesArray 和 entityArray 都是 (Entity*( 类型,所以当赋值完成时,只调用 Entity.operator=((,而我需要,在当前实体是 son1 的情况下,同时调用 Entity.operator=(( 和 EntitySon1.operator=((。
我知道我可以在数组上强制转换每个变量,以便调用正确的运算符,但这需要额外的信息来判断哪些实体是 son1,哪些是 son2,并且还需要为每个可能的实体的儿子手动键入。
有没有其他方法可以解决这个问题,或者我是否坚持这样做:
if (isSon1())
(EntitySon1)entityCopiesArray[i] = (EntitySon1)entitiesArray[i];
else if (isSon2())
(EntitySon2)entityCopiesArray[i] = (EntitySon2)entitiesArray[i];
// ... etc
编辑:我在深夜发表了这篇文章,并试图尽可能压缩信息,所以我说了一些不准确的话。我显然有一个实体*的数组,否则我将无法使用多态性。
请考虑以下事项:
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;
这些案例都不是特别好。你的帖子听起来像是在为一个糟糕的对象切片情况做准备。
不要做。
另一方面,如果您要做的是从列表中克隆对象:
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 << " dtordn"; }
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 << " dtordn"; }
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 scopen";
}
std::cout << "entities going out of scopen";
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
实例添加到组合中,我们可以在 Base
中实现 clone
成员 - 但请记住,您添加到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;
...
};
此时,Deferrd1 的实际内存中结构如下所示:
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)
{
}
我为Derived1
实现clone
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
,调用新的、空的克隆的复制 ctor rhs
当前对象并填写克隆。
我有一个实体数组,它只能是 son1 或 son2。
一般来说,这是不可能的。但是你可以有一个数组 Entity*
,每个数组都指向一个Son1
或一个Son2
。然后,您可以拥有一个虚拟clone
函数,以便此族的成员创建自身的副本并返回指向它的指针(类型为 Entity*
(。然后,您可以非常轻松地复制数组。