[英]C++ Interaction between two classes in a function
我試圖在這里做一個相對簡單的任務,並且不確定如何去做。
我有兩個興趣班, Parent
和Child
。 它們都是“人類”類的特例。 我這樣創建它們。
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);
}
這可行,但是我們必須為此付出沉重的代價。 Parent
和Child
必須了解彼此的內部結構才能正常工作。 這意味着,您不能輕易更改這些類,並且用戶很難擴展它們。 (他們需要研究整個課堂的內部知識,以確保它可以正常工作。)
此外,這種方法還有其他一些問題:
addParent
或addChild
函數中更改行順序會怎樣? (提示:我們保存該對象的副本,但未添加適當的元素。) 而且,我們現在正在使用類內部對象的副本。 這意味着,更新父級將使子級內部的實際數據保持不變(反之亦然)。 這可以通過使用指針部分解決,但也意味着您必須開始管理此簡單任務的指針...
方法方法
那么,什么是合適的解決方案? 一個簡單的選項是處理這些相互關聯的更新的功能。 采用第一個實現(不更新父級和子級),我們可以使用以下功能:
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;
};
這使我們可以將父子信息存儲在單獨的表中。 此外:
Child
和Parent
不需要對他們的內部知識一無所知。 另外,切換到指針很容易,因為只需要使用ParentalRelation
來管理它們。 這樣的優點是,在更改父或子時,始終具有正確的信息,而無需其他更新代碼。
免責聲明
ParentalRelation
方法可能非常困難。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.