我有以下最小版本的代碼(出于除錯目的,我已將所有資料欄位設定為公開):
#include <iostream>
#include <vector>
#include <string>
using std::cout; using std::vector; using std::string;
class Population; // forward declaration
class Person {
public:
Person *spouse;
void marry(Population &p, int i); // defined after Population class
};
class Population {
public:
vector<Person> people;
void add(Person p) {
people.push_back(p);
}
};
void Person::marry(Population &p, int i) {
*spouse = p.people.at(i);
}
int main() {
Person john; Person jane;
Population pop;
pop.add(john); pop.add(jane);
john.marry(pop, 1); // tries to marries Jane, but seg fault
}
它編譯得很好,但是由于以下行,它在運行時給出了分段錯誤:
*spouse = p.people.at(i);
究竟是什么導致了這個段錯誤,我該如何解決?
uj5u.com熱心網友回復:
*spouse = p.people.at(i);
這行代碼在 C 中執行以下操作:1) 回傳i對向量中第 th 個物件的參考p.people,以及 2) 將參考的物件復制到該spouse指標指向的另一個物件。
究竟是什么導致了這個段錯誤
嗯,將一個物件復制到另一個指標指向的物件的必要要求是,指標spouse必須是指向有效的現有物件的指標。
顯示的代碼中沒有任何內容將指標初始化為spouse指向某個現有物件。這肯定是程式崩潰的原因。
我怎樣才能繞過它?
spouse指標必須初始化為指向一個有效物件。然而,正如評論中所討論的,這些物件的整體設計存在一些缺陷,因此您可能需要完全重新設計物件的整個層次結構,以及各種物件之間的關系。
uj5u.com熱心網友回復:
有什么問題?
問題是以下陳述句并沒有按照您的想法執行:
*spouse = p.people.at(i);
該陳述句采用一個未初始化的指標spouse,并嘗試Person通過復制p.people.at(i).
由于spouse指標未初始化,因此這是 UB(未定義行為),而段錯誤是許多可能的癥狀之一。
如何解決?
您可能想要做的是將spouse配偶人的地址分配給指標:
spouse = &p.people.at(i);
對于您的小片段,這會更好。但這不是一個可靠的解決方案:如果您將新人添加到人口向量中,則向量可能會增長,并且在某些情況下,向量內容可能會移動到不同的位置,從而使之前獲得的所有指標指向其元素無效(如果您嘗試訪問它們,同樣是 UB)。
相反,您可以考慮將配偶保留為索引,或以保留地址的方式管理人口中的人員,例如在地圖中創建人員,或將向量設為可復制人員的地址向量 -執行時創建add()
uj5u.com熱心網友回復:
該問題是由取消參考未初始化的指標引起的。
整個班級的設計需要一點調整。否則,一個人可能會被復制,從而導致混淆一個人與誰結婚。
鑒于您想通過存盤指向伴侶的指標來表示婚姻,這意味著您有一些要求:
- 如果有關某人的資料存盤在不同的記憶體位置,則需要通知配偶
- 記憶體位置的任何移動都應該只讓一個人有效;否則您將無法確定配偶的配偶指標是否指向預期的人。
這個問題的一個解決方案是為Person.
class Person {
public:
Person() = default; // default constructor required by vector; also used for creating unmarried people
~Person()
{
if (spouse != nullptr)
{
// spouse now can no longer access their spouse
spouse->spouse = nullptr;
}
}
Person(Person&& old)
: spouse(old.spouse)
{
if (spouse != nullptr)
{
spouse->spouse = this; // tell spouse the new address of the partner
old.spouse = nullptr; // prevent destructor of old objects from doing incorrect updates to the spouse
}
}
Person& operator=(Person&& old)
{
if (spouse != nullptr)
{
spouse->spouse = nullptr;
}
spouse = old.spouse;
if (spouse != nullptr)
{
spouse->spouse = this; // tell spouse the new address of the partner
old.spouse = nullptr; // prevent destructor of old objects from doing incorrect updates to the spouse
}
return *this;
}
void marry(Person& newSpouse)
{
if (spouse != nullptr)
{
// still married, so throw an exception or divorce; I'll choose divorce here
spouse->spouse = nullptr;
}
spouse = &newSpouse;
spouse->spouse = this;
}
Person* GetSpouse()
{
return spouse;
}
private:
// spouse data managed internally; don't provide write access outsize of this class
Person *spouse {nullptr};
};
現在std::vector<Person>將自動更新后備存盤的大小,因為它使用移動語意......
std::vector<Person> population;
population.emplace_back();
population.emplace_back();
// note: the reference may be invalidated on a vector resize
auto& john = population[0];
auto& jane = population[1];
john.marry(jane);
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/450761.html
上一篇:Java模型類中的封裝
下一篇:針對不同型別的算術運算子多載c
