首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >动态分配的内存构造函数

动态分配的内存构造函数
EN

Stack Overflow用户
提问于 2018-09-04 04:16:44
回答 1查看 62关注 0票数 -2

我正在尝试创建一个构造函数,其中的字符串是动态分配的。我已经查找了几次动态分配的内存,并观看了有关它的视频,但我仍然不能百分之百地确定我是否理解了这个概念。我希望针对我正在编写的代码的示例能对我有所帮助。

以下是我的h文件中的私有变量:

代码语言:javascript
复制
string* tableID;
int numSeats;
string* serverName;

考虑到这一点,有人能告诉我如何为这个构造函数中的字符串动态分配内存吗?

代码语言:javascript
复制
Table::Table(const string& tableID, int numSeats, const string& serverName) {

}

最后,如果有人能告诉我动态分配内存的目的,我将不胜感激。我已经看到了关于什么是动态分配内存的解释,但我不理解它的用法。为什么要使用动态分配的内存?有什么福利待遇?它的缺点是什么?谢谢!

编辑:我将包含h文件的其余部分。请注意,这不是我创建的,所以我不能对其进行更改。我只能在cpp文件中遵守它。

代码语言:javascript
复制
#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;
};
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-09-04 04:53:52

获得所需内容的最简单方法是利用Member Initializer List,因为这也解决了具有相同名称的参数shadow the member variables的问题。

代码语言:javascript
复制
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%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

代码语言:javascript
复制
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;

然后是一个简单的类,它基本上完成了上面的工作,只需要更少的修饰。

代码语言:javascript
复制
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;
    }
};

和一个主要的练习它

代码语言:javascript
复制
int main()
{
    cout << "Bad example\n";
    try
    {
        bad_example("", "");
    }
    catch (...)
    {
        cout << "Caught exception\n";
    }
}

输出:

代码语言:javascript
复制
Bad example
Constructed
Caught exception

我们有一个构造的对象,并且永远不会销毁。

由于默认构造函数已由Table定义,因此我们可以使用支持C++11或更新标准的编译器,利用委托构造函数强制销毁部分构造的对象,因为它已完全由默认构造函数构造。

代码语言:javascript
复制
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;
    }
};

输出:

代码语言:javascript
复制
Good example
Constructed
Destructed
Caught exception

一次构造,一次销毁。这种方法的美妙之处在于,它具有很好的伸缩性,并且不会向您尚未拥有的代码中添加任何内容。成本最低,ab先初始化,然后分配,而不仅仅是初始化。如果更快的代码不能工作,那么它也是无用的。

完整示例:https://ideone.com/0ckSge

如果你不能编译成一个现代的标准,你最终会做一些类似下一个代码片段的事情,以确保所有的东西都被删除了。它的主要缺点是它很难看,但是当您添加更多必须构造和销毁的类时,它开始变得笨拙起来。

代码语言:javascript
复制
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;
    }
}

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52155773

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档