簡體   English   中英

C ++對象多態性問題

[英]C++ object polymorphism issue

所以我在理解如何解決我遇到的這種多態問題時遇到了一些問題。 簡而言之,我們只定義兩個級別的類,父親和兩個兒子:

父母:

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的情況下,同時具有Entity.operator = ()和EntitySon1.operator =()調用。

我知道我可以在數組上轉換每個變量,以便調用正確的運算符,但是需要額外的信息來判斷哪些實體是son1,哪些是son2,還需要為每個可能的實體的子進行手動輸入。

有沒有其他方法,或者我堅持做:

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

編輯:我在深夜做了這篇文章並試圖盡可能地壓縮信息所以我說的是不准確的事情。 我顯然有一個實體*的數組,否則我將無法使用多態。

我有一個實體數組,只能是son1或son2。

一般來說,這是不可能的。 但是你可以擁有一個Entity*數組,每個Entity*都指向一個Son1或一個Son2 然后,您可以擁有一個虛擬clone函數,以便該族的成員創建自身的副本並向其返回指針(類型為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;

這些案例都不是特別好。 你的帖子聽起來好像你正在設置自己的對象切片的壞情況。

不要做。

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

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實例,我們可以在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;
    ...
};

此時,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)
{
}

我為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 ,調用新的,空的,克隆的copy-ctor,當前對象為rhs並填寫克隆。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM