在构造函数中将指针/引用传递给现有对象的首选方法是什么?

pep*_*er0 16 c++

我将从例子开始.在boost中有一个很好的"tokenizer"类.它需要将一个字符串标记为构造函数中的参数:

std::string string_to_tokenize("a bb ccc ddd 0");
boost::tokenizer<boost::char_separator<char> > my_tok(string_to_tokenize);
/* do something with my_tok */
Run Code Online (Sandbox Code Playgroud)

字符串未在标记化器中修改,因此它由const对象引用传递.因此我可以在那里传递一个临时对象:

boost::tokenizer<boost::char_separator<char> > my_tok(std::string("a bb ccc ddd 0"));
/* do something with my_tok */
Run Code Online (Sandbox Code Playgroud)

一切都很好,但如果我尝试使用标记器,就会发生灾难.经过简短的调查,我意识到,tokenizer类存储了我给它的引用,并在进一步使用时使用.当然,它不能很好地引用临时对象.

文档没有明确说明在构造函数中传递的对象将在以后使用,但是,也没有说明,它不会是:)所以我不能假设这个,我的错误.

但是有点混乱.一般情况下,当一个对象通过const引用获取另一个对象时,它表明可以在那里给出临时对象.你怎么看?这是一个糟糕的习俗吗?在这种情况下,是否应该使用指向对象(而不是引用)的指针?或者更进一步 - 对于允许/禁止给临时对象作为参数的参数有一些特殊的关键字是不是很有用?

编辑:文档(版本1.49)相当简约,唯一可能表明这样的问题的部分是:

注意:在构造时实际上没有进行解析.解析是按需完成的,因为令牌是通过begin提供的迭代器访问的.

但它没有明确说明,将使用给定的相同对象.

然而,这个问题的重点是在这种情况下讨论编码风格,这只是启发我的一个例子.

Fre*_*abe 11

如果某个函数(例如构造函数)将参数作为引用引用,那么它应该

  • 清楚地记录引用对象的生命周期必须满足某些要求(如"在此之前未被销毁且发生这种情况")

要么

  • 如果需要在以后使用给定对象,请在内部创建副本.

在这种特殊情况下(boost::tokenizer类),我假设后者不是出于性能原因和/或使类可用于首先不可复制的容器类型.出于这个原因,我认为这是一个文档错误.


Ste*_*sop 8

就个人而言,我认为这是一个坏主意,并且最好编写构造函数来复制字符串,或者const std::string*取代.调用者只需要输入一个额外的字符,但该字符会意外地使用临时字符来阻止它们.

作为一项规则:不要在人们维护对象的情况下创造责任,而不要明确他们有责任.

我认为一个特殊的关键字不足以证明改变语言的合理性.实际上并不是临时性的问题,它的任何对象的生存时间都比构造的对象少.在某些情况下,临时会很好(例如,如果tokenizer对象本身也是同一个完整表达式中的临时对象).为了半个修复,我真的不想弄乱语言,并且有更全面的修复可用(例如,拿一个shared_ptr,虽然它有自己的问题).

"所以我不能假设这个,我的错误"

我认为这不是你的错误,我同意Frerich的意见,并且反对我的个人风格指南完全执行此操作,如果你这样做并且不记录那么这是任何合理的风格指南中的文档错误.

绝对必要的是,如果除了"至少与函数调用一样长"之外的任何其他内容,则记录所引用的参考函数参数的所需寿命.这是文档经常松懈的事情,需要妥善完成以避免错误.

即使在垃圾收集语言中,生命周期本身也会自动处理,因此往往会被忽略,因此无论您是否可以更改或重复使用对象而不改变将其传递给方法的其他对象的行为,过去的某个时候.因此,函数应该记录它们是否在任何缺乏引用透明性的语言中保留其参数的别名.特别是在C++中,对象生存期是调用者的问题.

不幸的是,实际确保您的函数无法保留引用的唯一机制是传递值,这具有性能成本.如果您可以发明一种通常允许别名的语言,但也有一个restrict在编译时强制执行的C风格属性,const-style,以防止函数对其参数的引用,那么祝你好运并注册我.