簡體   English   中英

函數中兩個類之間的C ++交互

[英]C++ Interaction between two classes in a function

我試圖在這里做一個相對簡單的任務,並且不確定如何去做。

我有兩個興趣班, ParentChild 它們都是“人類”類的特例。 我這樣創建它們。

main.cpp的相關位:

int main() {
    //create an object of class Parent parent1 with a name Albert.
    Parent parent1("Albert");

    //create an object of a class Child child1 named John.
    Child child1("John");
    return 0;
}

Parent.h標頭包含一個名為addChild()的函數。 它所做的只是將一個子代添加到Parent對象內的Children的向量中。

Parent.h的相關位:

class Parent: public Human {
public:
    vector<Human> Children;

   ...
void addChild(Human aHuman){
        Children.push_back(aHuman);
    }

  };

我想要實現的是addChild()函數同時向該子級添加一個父級。 也就是說,要做類似的事情:

void addChild(Human aHuman) {
        Children.push_back(aHuman);
        aHuman.Parent = MYSELF;
    }

以上,MYSELF表示我不知道如何編碼的位。

所以當我運行這個:

int main() {
    //create an object of class Parent parent1 with a name Albert.
    Parent parent1("Albert"); 

    //create an object of a class Child child1 named John.
    Child child1("John");

    parent1.addChild(child1);

    return 0;
}

我希望將艾伯特設為約翰的父母,並將約翰設為艾伯特的孩子。

誰能幫我這個?

編輯:

根據Jan的建議的代碼:

 #include <iostream> #include<vector> class Human{ }; class Child; class Parent : public Human { std::vector<Child> children; public: friend Child; void addChild(Child& child) { children.emplace_back(child); child.parents.emplace_back(*this); }; }; class Child : public Human { std::vector<Parent> parents; public: friend Parent; void addParent(Parent& parent) { parents.emplace_back(parent); parent.children.emplace_back(*this); } ; }; 

錯誤:

 In member function 'void Parent::addChild(Child&)': invalid use of incomplete type 'class Child' child.parents.emplace_back(*this); error: forward declaration of 'class Child' class Child; 

我擔心您會遇到一個XY問題,並且通常將對象多態性和C ++設計的重要概念混合在一起,但是,這是您所要求的解決方案:

#include <vector>
#include <memory>

struct Human {
    virtual ~Human() = default;
};

struct Parent;

struct Child: Human{
    Child(std::string) {}
    void addParent(std::weak_ptr<Parent> aParent) {
        Parent = aParent;
    }
private:
    std::weak_ptr<Parent> Parent;
};

struct Parent: Human, std::enable_shared_from_this<Parent> {
    Parent(std::string) {}
    void addChild(Child aChild){
        aChild.addParent(shared_from_this());
        Children.push_back(aChild);

    }
private:
    std::vector<Child> Children;
};



int main() {
    auto parent1 = std::make_shared<Parent>("Albert"); //create an object of class Parent parent1 with a name Albert.
    Child child1("John");//create an object of a class Child child1 named John.
    parent1->addChild(child1);
}

它不是很漂亮,但是更多的是您設計中的問題,即問題。

請注意,對於所有此類調用,無論是按值,復制,生存期還是與Human類無關,此代碼仍遠非最佳。 但是,更好的解決方案將需要更多的上下文和設計。

更新 Jahns代碼的更新Jahns代碼從C ++ 11到C ++ 14無效:正如您問的jans代碼有什么問題:它無法編譯,因為它通常無法正常工作。 因為std::vector<T>需要完整的類型,並且基本上等效於存儲T的副本。 從C ++ 17:Jahns代碼開始運行,但是因為現在std::vector<T>也可以用作存儲對T的引用,基本上等效於下面描述的技巧。

由於您的設計,您有2個具有循環依賴關系的類:子代應保存其父母,父母應為其子代。 因此,您即Jan決定將孩子的父母副本保存在孩子的父母中。 但是為了保存它,計算機必須知道它有多大。

因此,孩子必須知道父母有多大才能知道自己將有多大,但是要知道父母有多大,就必須先知道自己有多大,因為它也保存在父母中。 看到問題了嗎? 自我參考是一個魔鬼的游樂場,通常會導致無限遞歸。

然而,打破循環的一個訣竅是,對孩子說:“不要保存整個父母,只需引用它,這樣就不必知道它有多大。” 這是通過parent的向前減速以及智能指針的使用來完成的,這些智能指針使您免於做壞事,例如嘗試使用空白引用。 (那樣的話,像Parent*這樣的原始指針是不好的,不要使用它們,您可能會得到未定義的行為 ,例如,在星期五2函數調用后期出現seg錯誤)

另一個非常相似的通用技巧是,孩子和父母都同意同一根,就像人類的基層階級一樣。 它還通過使用引用解決了該問題。 您的班級建議您首先嘗試,否則您的老師敦促您采用這種方式。

但是,所有這些技巧都會增加復雜性,並降低性能和安全性,因為您將工作從編譯時推到了運行時。 Java,C#,Python和Co.甚至默認情況下都這樣做。 使反模式更常見,恕我直言。

我只能重復一遍:我建議讀一本有關C ++的書,Lipman撰寫的C ++ Primer是我作為初學者閱讀的書。

您主要有兩個問題:

  • 您按值傳遞而不是引用傳遞,因此僅修改副本
  • 您有對象切片: std::vector<Human>僅存儲Parent / Child Human部分。

當您在不使用&的情況下使用void addChild(Human aHuman)時,您會遇到問題;如果在不使用&的情況下將保存在對象中的更改更改為2,則需要在函數調用中復制對象的1倍,並執行void addChild(Human&aHuman)。當您插入addChild(child1); MYSELF可以是這個,但是您需要更改向量而不是獲取對象

您遇到了新手OOP程序員的一個常見問題:您嘗試通過擴展OOP類而不是創建關系類來解決關系問題。

新手方法

首先,讓我們繼續您的方法一點……當然,每個Parent都有一個Child ,每個Child都有一個Parent ,並且自然的方式似乎將這些關系存儲在每個類中。 從而:

class Parent : public Human
{
    std::vector<Child> children;
  public:
    void addChild(const Child& child);
};

class Child : public Human
{
    std::vector<Parent> parents;
  public:
    void addParent(const Parent& parent);
};

現在,讓我們實現add...函數:

void Child::addParent(const Parent& parent)
{
    parents.emplace_back(parent);
}
void Parent::addChild(const Child& child)
{
    children.emplace_back(child);
}

如前所述,這要求您分別更新父級和子級。 因此,讓我們改變我們的方法,並讓班級成為朋友:

class Child;
class Parent : public Human
{
    std::vector<Child> children;
  public:
    friend Child;
    void addChild(Child& child);
};

class Child : public Human
{
    std::vector<Parent> parents;
  public:
    friend Parent;
    void addParent(Parent& parent);
};

現在,該朋友關系也可以用於更新其他類:

void Child::addParent(Parent& parent)
{
    parents.emplace_back(parent);
    parent.addChild(*this);
}
void Parent::addChild(Child& child)
{
    children.emplace_back(child);
    child.addParent(*this);
}

該死,這行不通! 發生了什么? 我們有效地創建了一個無限循環。 因為將Child添加到Parent會添加子級,還需要在子addParent上調用addParent函數,然后添加父級,還必須在父級上調用addChild函數,從而添加一個子級,...點。

我們該如何解決? 一種方式是添加子級/父級:

void Child::addParent(Parent& parent)
{
    parents.emplace_back(parent);
    parent.children.emplace_back(*this);
}
void Parent::addChild(Child& child)
{
    children.emplace_back(child);
    child.parents.emplace_back(*this);
}

這可行,但是我們必須為此付出沉重的代價。 ParentChild必須了解彼此的內部結構才能正常工作。 這意味着,您不能輕易更改這些類,並且用戶很難擴展它們。 (他們需要研究整個課堂的內部知識,以確保它可以正常工作。)

此外,這種方法還有其他一些問題:

  • 如果我們在addParentaddChild函數中更改行順序會怎樣? (提示:我們保存該對象的副本,但未添加適當的元素。)
  • 您如何從父母中刪除孩子並正確地更新孩子的父母?
  • 作為父母,您如何確定孩子不會以怪異的方式改變您的內部行為,反之亦然?

而且,我們現在正在使用類內部對象的副本。 這意味着,更新父級將使子級內部的實際數據保持不變(反之亦然)。 這可以通過使用指針部分解決,但也意味着您必須開始管理此簡單任務的指針...

方法方法

那么,什么是合適的解決方案? 一個簡單的選項是處理這些相互關聯的更新的功能。 采用第一個實現(不更新父級和子級),我們可以使用以下功能:

void linkChildToParent(Parent& parent, Child& child)
{
    parent.addChild(child);
    child.addChild(parent);
}

linkChildToParent函數...

  • 將正確更新您的孩子和您的父母。
  • 不依賴於行順序。
  • 易於閱讀和維護。
  • 對於“ Child和“ Parent用戶來說很容易擴展。

這種方法解決了我們的大多數問題,甚至可以擴展為正確,輕松地處理指針。 但是,我們仍然需要進行一些工作來更新合適的成員,...如何解決? 當然要通過適當的OOP!

OOP方法

我們遇到的問題都與我們存儲父母與子女之間的關系的方式有關。 這表明我們的抽象是錯誤的! 考慮一下,我們可以提出正確的抽象, ParentalRelation類:

//Given, that we only have basic functionality, a struct is sufficient here...
struct ParentalRelation
{
    Child child;
    Parent parent;
};

這使我們可以將父子信息存儲在單獨的表中。 此外:

  • ChildParent不需要對他們的內部知識一無所知。
  • 擴展任何類都很容易。
  • 刪除或更改父母/子女非常容易。
  • 坦率地說,要理解和編寫的代碼要少得多。

另外,切換到指針很容易,因為只需要使用ParentalRelation來管理它們。 這樣的優點是,在更改父或子時,始終具有正確的信息,而無需其他更新代碼。

免責聲明

  • 我希望這有點清楚。
  • 盡管在這里看起來很容易,但是在實踐中找到ParentalRelation方法可能非常困難。

暫無
暫無

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

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