动态分配的内存构造函数

Dynamically Allocated Memory Constructor

本文关键字:构造函数 内存 动态分配      更新时间:2023-10-16

我正在尝试创建一个动态分配字符串的构造函数。我已经多次查找动态分配的内存并观看了有关它的视频,但我仍然不能 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;
};

获取所需内容的最简单方法是利用成员初始值设定项列表,因为这还解决了让参数隐藏同名成员变量的问题。

Table::Table(const string& tableID, 
int numSeats, 
const string& serverName):
tableID(new string(tableID)), 
numSeats(numSeats), 
serverName(new string(serverName))
{
}

使用new运算符执行分配。稍后,您必须使用delete运算符释放动态分配的内存。这是有关new的文档,delete也是如此。

但是使用指针要求很奇怪,因为存储指向string的指针会使您在类中执行的其他所有事情变得更加困难。这可能是作业的重点,但有更好、更少混淆的方法来教授这一课。

必须释放分配的string。资源分配是初始化(资源获取是初始化(RAII(是什么意思(的C++习语建议你有一个析构函数来自动清理以确保它完成。如果你需要一个析构函数,你几乎总是需要三巨头的另外两个成员(什么是三人法则?(,并且可能还需要考虑五法则。

string为你遵守五法则,你应该能够利用零法则,不实现任何特殊功能。

M.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 << "Constructedn";
}
~throwsecond()
{
cout << "Destructedn";
}
};
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 examplen";
try
{
bad_example("", "");
}
catch (...)
{
cout << "Caught exceptionn";
}
}

输出:

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

一个构造和一个破坏。这种方法的优点在于它可以很好地扩展,并且不会向您尚未拥有的代码添加任何内容。成本是最小的,ab初始化然后分配,而不仅仅是初始化。如果代码不起作用,更快的代码就毫无用处。

完整示例: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;
}
}

可能有一种方法可以改进这一点并使其更易于管理,但我不知道。我只是在可能的情况下使用更新的标准和价值语义(如果有人可以提供描述这个概念的良好链接,我会很高兴(。