当前位置:  开发笔记 > 编程语言 > 正文

何时通过引用传递不是一个好主意?

如何解决《何时通过引用传递不是一个好主意?》经验,为你挑选了2个好方法。

这是一个我从未真正理解过的内存分配问题.

void unleashMonkeyFish()  
{  
    MonkeyFish * monkey_fish = new MonkeyFish();
    std::string localname = "Wanda";  
    monkey_fish->setName(localname);  
    monkey_fish->go();  
}  

在上面的代码中,我在堆上创建了一个MonkeyFish对象,为它指定了一个名称,然后在世界上释放它.假设分配的内存的所有权已经转移到MonkeyFish对象本身 - 只有MonkeyFish本身将决定何时死亡并自行删除.

现在,当我在MonkeyFish类中定义"name"数据成员时,我可以选择以下之一:

std::string name;
std::string & name;

当我在MonkeyFish类中定义setName()函数的原型时,我可以选择以下之一:

void setName( const std::string & parameter_name );
void setName( const std::string parameter_name );

我希望能够最小化字符串副本.事实上,如果可以的话,我想完全消除它们.所以,似乎我应该通过引用传递参数......对吗?

让我感到困惑的是,一旦unleashMonkeyFish()函数完成,我的localname变量似乎将超出范围.这是否意味着我强行要通过副本传递参数?或者我可以通过引用传递它并以某种方式"逃脱它"?

基本上,我想避免这些情况:

    我不想设置MonkeyFish的名称,只是当unleashMonkeyFish()函数终止时,localname字符串的内存消失.(这似乎非常糟糕.)

    如果我可以帮助它,我不想复制字符串.

    我宁愿不要新的localname

我应该使用什么原型和数据成员组合?

澄清:有几个答案建议使用static关键字来确保在unleashMonkeyFish()结束时不会自动解除分配内存.由于此应用程序的最终目标是释放N MonkeyFish(所有这些都必须具有唯一名称),因此这不是一个可行的选择.(是的,MonkeyFish - 变幻无常的生物 - 通常会在一天内改变他们的名字,有时会改变几次.)

编辑:Greg Hewgil指出将name变量存储为引用是非法的,因为它没有在构造函数中设置.我在问题中留下了错误,因为我认为我的错误(和格雷格的纠正)可能对第一次看到这个问题的人有用.



1> Johannes Sch..:

一种方法是使用你的字符串

std::string name;

作为对象的数据成员.然后,在unleashMonkeyFish函数中创建一个类似于你所做的字符串,并像你展示的那样通过引用传递它

void setName( const std::string & parameter_name ) {
    name = parameter_name;
}

它会做你想要的 - 创建一个副本来将字符串复制到你的数据成员.如果您指定另一个字符串,则不必在内部重新分配新缓冲区.可能,分配新字符串只会复制几个字节.std :: string具有保留字节的能力.所以你可以叫"name.reserve(25);" 在你的构造函数中,如果你指定更小的东西,它可能不会重新分配.(我已经完成了测试,如果你从另一个std :: string分配,看起来像GCC总是重新分配,但是如果你从一个c-string分配它就不会.他们说他们有一个写时复制字符串,这可以解释为行为).

您在unleashMonkeyFish函数中创建的字符串将自动释放其分配的资源.这是这些对象的关键特征 - 他们管理自己的东西.类有一个析构函数,它们用于在对象死亡时释放已分配的资源,std :: string也是如此.在我看来,你不应该担心在函数中有std :: string local.无论如何,它不会对你的表现做任何明显的事情.一些std :: string实现(msvc ++ afaik)具有小缓冲区优化:对于一些小的限制,它们将字符保存在嵌入式缓冲区中,而不是从堆中分配.

编辑:

事实证明,对于具有高效swap实现(恒定时间)的类,有更好的方法:

void setName(std::string parameter_name) {
    name.swap(parameter_name);
}

这更好的原因是,现在调用者知道正在复制参数.现在,编译器可以轻松应用返回值优化和类似的优化.例如,考虑这种情况

obj.setName("Mr. " + things.getName());

如果你有setName一个引用,那么在参数中创建的临时将绑定到该引用,并且在setName其中将被复制,并且在它返回之后,临时将被销毁 - 无论如何这都是丢弃的产品.这只是次优,因为可以使用临时本身而不是其副本.使参数不是引用将使调用者看到正在复制参数,并使优化器的工作更容易 - 因为它不必内联调用以查看参数是否仍然被复制.

有关进一步说明,请阅读优秀文章 BoostCon09/Rvalue-References



2> Greg Hewgill..:

如果使用以下方法声明:

void setName( const std::string & parameter_name );

那么你也会使用成员声明:

std::string name;

setName身体中的任务:

name = parameter_name;

您不能将该name成员声明为引用,因为您必须在对象构造函数中初始化引用成员(这意味着您无法将其设置为setName).

最后,您的std::string实现可能仍然使用引用计数字符串,因此在赋值中不会生成实际字符串数据的副本.如果您担心性能,最好熟悉您正在使用的STL实现.

推荐阅读
云聪京初瑞子_617
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有