我正在尝试创建一个构造函数,其中的字符串是动态分配的。我已经查找了几次动态分配的内存,并观看了有关它的视频,但我仍然不能百分之百地确定我是否理解了这个概念。我希望针对我正在编写的代码的示例能对我有所帮助。
以下是我的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;
};
发布于 2018-09-04 04:53:52
获得所需内容的最简单方法是利用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
%s。资源分配的C++惯用法是初始化(What is meant by Resource Acquisition is Initialization (RAII)?),它建议您使用析构函数来自动化清理,以确保清理完成。如果你需要一个析构函数,你几乎总是需要三巨头(What is The Rule of Three?)的另外两个成员,并且可能还需要考虑The Rule of Five。
然而,因为string
为您遵守了规则5,所以您应该能够利用Rule of Zero,并且不实现任何特殊函数。
M.在评论中提出了一个很好的观点。上面的例子太天真了。这可能是赋值所需的全部内容,但对于真正的代码来说还不够好。它迟早会失败。它如何失败的示例。
首先,我们用可以暴露错误的东西替换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;
}
}
可能有一种方法可以改进这一点,使其更易于管理,但我不知道。我只是在可能的情况下使用更新的标准和值语义(如果有人能提供一个很好的链接来描述这个概念,我会很高兴)。
https://stackoverflow.com/questions/52155773
复制相似问题