动态分配的内存构造函数

时间:2018-09-03 20:16:44

标签: c++

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

1 个答案:

答案 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 newthe 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

一个构造和一个破坏。这种方法的好处是它可以很好地扩展,并且不会向您没有的代码中添加任何内容。成本极低,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;
    }
}

可能有一种方法可以对此进行改进并使它更易于管理,但是我不知道。我会尽可能使用新的标准和价值语义(如果有人可以提供描述此概念的良好链接,我会喜欢的)。