这令人费解。我需要在我的程序中使用函数CCountry :: getName()。奇怪的是,在测试它是否可以工作时,它可以在一个地方工作,但不会工作两行,我无法弄清楚原因。例如......
while(line != "---" && line != "------")
{
CCountry *tempCountry = new CCountry(line);
cout << tempCountry->getName() << flush;
(*tempContinent).addCountry(*tempCountry);
getline(filestr, line);
}
作品。它按顺序列出了所有国家/地区名称。然而...
while(line != "---" && line != "------")
{
CCountry *tempCountry = new CCountry(line);
(*tempContinent).addCountry(*tempCountry);
getline(filestr, line);
cout << tempCountry->getName() << flush;
}
不起作用。它甚至无法打印一个国家名称,而是在调用getName()的行中抛出一个seg错误。
这里有两个函数,getName()和addCountry()
供进一步参考string CCountry::getName()
{
return *name;
}
和
void CContinent::addCountry(CCountry country)
{
(*countries).push_back(country);
}
根据请求,这是CCountry构造函数:
CCountry::CCountry(string in_name)
{
name = new string;
*name = in_name;
player = new int;
*player = -1;
units = new int;
*units = 0;
neighbors = new list<CCountry>;
}
答案 0 :(得分:3)
我可能会对这段代码中的一长串错误发出嘎嘎声,但导致你错误的那一行最终是因为以下原因:
您的CCountry课程没有练习Rule of 3,必须,因为它有动态分配的成员。(顺便说一下,甚至不需要)
您正在通过按值获取国家/地区的成员函数将您的CCounty对象添加到您的大陆。此时制作对象的浅表副本。然后将其推入非洲大陆的容器中,这会产生另一个浅色副本。在addCountry()退出时,原始浅拷贝被销毁,并且在返回到您的调用代码的过程中,CCountry对象的内部已被破坏。因此,你的本地(不应该首先动态分配,顺便说一句,顺便说一句)是正式的。
猜猜是什么......你的大陆集装箱里面的那个。
我可能会首先考虑CCountry对象本身。我个人会在CContinent类而不是CCountry中管理一个CCountry的邻居,因为无论如何管理CCountry对象的集合,而是管理它们自己的对象。如果您决定坚持使用当前模型,CCountry的潜在替代方案可能是这样的:
class CCountry
{
public:
CCountry(const std::string& name)
: name(name), player(0), units(0)
{
}
// properties
const std::string& getName() const { return name; };
int getPlayer() const { return player; };
void setPlayer(int player) { this->player = player; };
int getUnits() const { return units; };
void setUnits(int units) { this->units = units; };
// neighbor access
const std::list<const CCountry*> getNeighbors() const
{
std::list<const CCountry*> res;
for (auto it=neighbors.begin(); it != neighbors.end(); ++it)
res.push_back(it->second);
return res;
}
// adding a new neighbor
void addNeighbor(const CCountry& other)
{
neighbors[ other.getName() ] = &other;
}
private:
std::string name;
int player;
int units;
std::map<std::string, const CCountry*> neighbors;
};
但是请注意:追求这样的模型(正如你所看到的,你的原始模型),将会有潜在的陷阱,特别是CCountry可能指向另一个CCountry的技术上它没有'拥有。这就是为什么我更喜欢邻居关联由CContinent类本身管理,因为它将拥有CCountry和他们的邻居协会。
答案 1 :(得分:2)
我怀疑你已经定义了这样的CCountry
析构函数:
~CCountry() {
delete name;
delete player;
delete units;
delete neighbors;
}
但我怀疑你有不为CCountry
定义了一个副本构造函数。这意味着编译器正在生成这样的复制构造函数:
CCountry(CCountry const &that) :
name(that.name),
player(that.player),
units(that.units),
neighbors(that.neightbors)
{ }
现在,CContinent::addCountry
定义为CCountry
,而不是CCountry &
。因此,当您执行(*tempContinent).addCountry(*tempCountry)
时,您的程序将使用该编译器定义的*tempCountry
复制构造函数生成CCountry
的(临时)副本。
所以现在你的程序有两个独立的CCountry
实例:一个由tempCountry
指向,另一个在CContinent::addCountry
的{{1}}参数中。但由于编译器定义的复制构造函数的工作方式,两个实例都有country
个成员变量指向同一个name
实例。
删除临时副本后,其析构函数将删除该字符串实例。现在string
指向的实例在其tempCountry
成员变量中有一个悬空指针。当您尝试取消引用name
中的悬空指针时,行为未定义,并导致您的分段错误。
将您的getName
,name
,player
和units
成员变量更改为而非成为指针。它们应该只是普通类型,如下:
neighbors
您可能还希望将功能更改为引用而不是副本。
答案 2 :(得分:0)
是否有可能在CCountry的构造函数中导致某种覆盖?听起来对我来说。
答案 3 :(得分:0)
在CCountry的构造函数中,您使用new string
分配名称,在析构函数中,您可以使用delete name
释放它。我不知道为什么你需要这样做,将string
而不是string*
存储为name
的成员CCountry
可能更简单。当您将CCountry
作为参数传递给CContinent::addCountry
时,将创建临时副本,然后将其删除,这将导致删除在CCountry::name
的多个实例之间共享的CCountry
。为避免这种情况,您需要使用string
代替string*
作为name
的成员CCountry
,或者实施您自己的CCountry复制构造函数。
答案 4 :(得分:0)
此代码存在许多问题,但对于初学者来说,
CCountry
是否具有值语义或是实体类型。
在第一种情况下,你不应该指向它,或者
使用new
动态分配它。你应该确保这一点
它可以被正确地复制和分配。在第二个,你
不应该通过值传递给CContinent::addCountry
(和你
应该通过制作副本来禁止复制和分配
构造函数和赋值运算符私有,或通过派生
来自boost::noncopyable
)。
您没有显示CCountry
的定义,而是您的方式
初始化name
表示您正在假设
std::string
是一个实体对象。它不是 - 它有价值
语义,几乎没有你会有的情况
指向std;:string
的指针。 (唯一的例外是a
函数参数或返回值,您想要支持的位置
一个空指针,用于指示值的缺失。)相同
适用于player
,units
和neighbors
:上下文
你将有一个指向int
或标准的指针
容器仅限于需要空指针的情况
表明没有价值。
您也不显示使用复制构造函数,赋值
运算符或析构函数。如果您要删除内存中的内存
析构函数,并没有复制构造函数,这是
问题的根源。编译器生成了复制构造函数
做一个浅拷贝,这意味着当你打电话
CContinent::addCountry
,你最终得到两个对象
相同的指针。当参数被破坏时,如果它被删除
任何事情,这会使作为参数传递的对象无效(其中
包含相同的指针)。有不同的处理方式
这,但几乎在所有情况下,最合适的是不
使用指针。 (例如,班级std::string
有副本
执行深层复制的构造函数,因此使用它没有问题。)
最后,在一个完全不相关的问题上:在你的课程前加上
C
的名称不是个好主意。微软采用了这一点
他们的班级名称的约定(至少在他们的一些
库),任何看到像CCountry
这样名字的读者都会
假设它是来自其中一个Microsoft库的类,
并将尝试在Microsoft文档中找到它,而不是
在你的代码中。