我正在尝试创建一个在其中动态分配字符串的构造函数。我已经多次查找动态分配的内存并观看了有关它的视频,但是我仍然不确定100%是否了解这个概念。我希望有一个特定于我正在编写的代码的示例可以对我有所帮助。
这些是我在h文件中拥有的私有变量:
string* tableID;
int numSeats;
string* serverName;
牢记这一点,有人可以告诉我如何为该构造函数中的字符串动态分配内存吗?
Table::Table(const string& tableID, int numSeats, const string& serverName) {
}
最后,如果有人可以告诉我动态分配内存的目的,我将不胜感激。我已经看到了关于什么是动态分配内存的解释,但是我不了解它的用法。为什么要使用动态分配的内存?有什么好处?有什么缺点?谢谢!
编辑:我包括了h文件的其余部分。请注意,这不是我创建的,因此无法对其进行更改。我只能在cpp文件中坚持使用它。
#include <string>
#include "party.h"
using std::string;
class Table {
public:
Table();
Table(const string& tableID, int numSeats, const string& serverName);
~Table();
const string* getTableID() const { return tableID; }
int getNumSeats() const { return numSeats; }
const string* getServerName() const { return serverName; }
void decrementTimer() { timer--; }
int getTimer() const { return timer; }
void setTimer(int duration) { timer = duration; }
const Party* getParty() { return party; }
void seatParty(const Party* newParty);
void clearTable() { party = nullptr; timer = 0; }
private:
string* tableID;
int numSeats;
string* serverName;
int timer;
const Party* party;
};
答案 0 :(得分:0)
获得所需内容的最简单方法是利用Member Initializer List,因为这也解决了具有相同名称的参数shadow the member variables的问题。
Table::Table(const string& tableID,
int numSeats,
const string& serverName):
tableID(new string(tableID)),
numSeats(numSeats),
serverName(new string(serverName))
{
}
使用new
运算符执行分配。稍后,您将必须使用delete
运算符释放动态分配的内存。 Here is documentation on new
和the same for delete
.
但是使用指针的要求很奇怪,因为存储指向string
的指针会使类中的其他所有对象难于执行数量级。这可能是作业的重点,但是有更好和更少混乱的方法来教授本课。
必须释放分配的string
。资源分配是初始化(What is meant by Resource Acquisition is Initialization (RAII)?)的C ++习惯用法建议您有一个析构函数来自动执行清理操作,以确保清理完成。如果需要析构函数,几乎总是需要三巨头(What is The Rule of Three?)的其他两个成员,并且可能还需要考虑The Rule of Five。
由于string
会为您遵守5规则,因此您应该能够利用Rule of Zero的优势并且不执行任何特殊功能。
MM在评论中提出了一个很好的观点。上面的例子太幼稚了。这可能是分配所需要的全部,但是对于实际代码来说还不够好。迟早它会失败。失败示例。
首先,我们将string
替换为可能暴露错误的内容:
class throwsecond
{
static int count;
public:
throwsecond(const string &)
{
if (count ++)
{
count = 0; // reset count so only every second fails
throw runtime_error("Kaboom!");
}
cout << "Constructed\n";
}
~throwsecond()
{
cout << "Destructed\n";
}
};
int throwsecond::count = 0;
然后是一个简单的类,基本上没有多余的装饰即可完成上述操作
class bad_example
{
throwsecond * a;
throwsecond * b;
public:
bad_example(): a(nullptr), b(nullptr)
{
}
bad_example (const string& a,
const string& b)
{
this->a = new throwsecond(a);
this->b = new throwsecond(b);
}
~bad_example()
{
delete a;
delete b;
}
};
和锻炼它的主体
int main()
{
cout << "Bad example\n";
try
{
bad_example("", "");
}
catch (...)
{
cout << "Caught exception\n";
}
}
输出:
Bad example
Constructed
Caught exception
我们构造了一个从未破坏过的物体。
由于Table
定义了默认构造函数,因此我们可以使用支持C ++ 11或更高版本标准的编译器,利用委托构造函数来强制破坏部分构造的对象,因为它已由默认构造函数完全构造。
class good_example
{
throwsecond * a;
throwsecond * b;
public:
good_example():
a(nullptr), b(nullptr) //must be nulled or destruction is dicey
{
}
good_example (const string& a,
const string& b) : good_example() // call default constructor
{
this->a = new throwsecond(a);
this->b = new throwsecond(b);
}
~good_example()
{
delete a;
delete b;
}
};
输出:
Good example
Constructed
Destructed
Caught exception
一个构造和一个破坏。这种方法的好处是它可以很好地扩展,并且不会向您没有的代码中添加任何内容。成本极低,a
和b
被初始化,然后分配,而不是初始化。如果无法运行,更快的代码将毫无用处。
完整示例:https://ideone.com/0ckSge
如果您不能按照现代标准编译,那么您最终会做类似的事情 下一个代码段,以确保所有内容均已删除。它的主要缺点是丑陋,但是随着您添加更多必须构造和销毁的类,它开始变得笨拙。
Table::Table(const string& tableID,
int numSeats,
const string& serverName):
tableID(NULL),
numSeats(numSeats),
serverName(NULL)
{
try
{
this->tableID(new string(tableID)),
// see all the this->es? don't shadow variables and you won't have this problem
// miss a this-> and you'll have a really bad day of debugging
this->serverName(new string(serverName))
// more here as required
}
catch (...)
{
delete this->tableID;
delete this->serverName;
// more here as required
throw;
}
}
可能有一种方法可以对此进行改进并使它更易于管理,但是我不知道。我会尽可能使用新的标准和价值语义(如果有人可以提供描述此概念的良好链接,我会喜欢的)。