首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >std::string类成员应该是指针吗?

std::string类成员应该是指针吗?
EN

Stack Overflow用户
提问于 2015-12-15 21:10:27
回答 4查看 5.4K关注 0票数 9

为什么/为什么不呢?

假设我有一个类,它在构造函数中接受一个字符串并存储它。这个类成员是一个指针还是一个值?

代码语言:javascript
复制
class X {
    X(const std::string& s): s(s) {}
    const std::string s;
};

或者..。

代码语言:javascript
复制
class X {
    X(const std::string* s): s(s) {}
    const std::string* s;
};

如果我要存储一个原始类型,我会拿一份拷贝。如果我要存储一个对象,我会使用一个指针。

我觉得我想复制那根线,但我不知道什么时候该决定。我要复制矢量吗?布景?地图?整个JSON文件?

编辑:

听起来我需要读一下移动语义学。但无论如何,我想把我的问题说得更具体一点:

如果我有一个10兆字节的文件作为一个const字符串,我真的不想复制它。

如果我正在增加100个对象,将一个5个字符的const字符串传递到每个对象的构造函数中,那么它们都不应该有所有权。可能只需要一份字符串的副本。

所以(假设我并没有完全错)显然应该在类之外做些什么,但是当您设计class GenericTextHaver时,如何决定文本拥有的方法呢?

如果您所需要的只是一个类,该类在其构造函数中接受一个const字符串,并允许您从中获得一个具有相同值的const字符串,那么如何决定如何在内部表示它呢?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2015-12-15 22:53:16

std::string类成员应该是指针吗?

不是

有何不可呢?

因为std::string和标准库中的每个其他对象一样,以及c++中所有其他编写良好的对象都被设计为一个值。

它可能在内部使用指针,也可能不使用指针--这不是您关心的问题。你需要知道的是,它写得很漂亮,而且表现得非常高效(实际上比你现在想象的还要高效),当你把它当作一个值处理时……尤其是如果你用移动建筑。

我觉得我想复制那根线,但我不知道什么时候该决定。我要复制矢量吗?布景?地图?整个JSON文件?

是。一个编写良好的类具有“值语义”(这意味着它被设计为被当作值对待),因此复制和移动。

很久以前,当我第一次写代码时,指针通常是让计算机快速完成某些事情的最有效的方法。如今,由于内存缓存、管道和预取,复制几乎总是更快。(是的,真的!)

在多处理器环境中,除了最极端的情况外,复制的速度要快得多。

如果我有一个10兆字节的文件作为一个const字符串,我真的不想复制它。

如果您需要副本,那么就复制它。如果你真的想移动它,那么std::move它。

如果我正在增加100个对象,将一个5个字符的const字符串传递到每个对象的构造函数中,那么它们都不应该有所有权。可能只需要一份字符串的副本。

一个5字符的字符串复制起来非常便宜,你甚至不应该去想它。复制就行了。信不信由你,编写std::string时完全知道大多数字符串都很短,而且经常被复制。甚至不会有任何涉及的内存分配。

所以(假设我没有完全错)显然应该从类之外做些什么,但是当您设计类GenericTextHaver时,如何决定文本拥有的方法呢?

以最优雅的方式表达代码,简洁地传达您的意图。让编译器来决定机器代码的外观--这是它的工作。成千上万的人花了时间来确保它比你以往任何时候都做得更好。

如果您所需要的只是一个类,该类在其构造函数中接受一个const字符串,并允许您从中获得一个具有相同值的const字符串,那么如何决定如何在内部表示它呢?

几乎在所有情况下,都要存储副本。如果两个实例实际上需要共享相同的字符串,那么可以考虑其他一些东西,比如std::shared_ptr。但在这种情况下,它们可能不仅需要共享一个字符串,所以“共享状态”应该封装在其他对象中(最好是使用值语义!)

好了,别说了-给我看看这门课该怎么看

代码语言:javascript
复制
class X {
public:

    // either like this - take a copy and move into place
    X(std::string s) : s(std::move(s)) {}

   // or like this - which gives a *miniscule* performance improvement in a
   // few corner cases
/*
   X(const std::string& s) : s(s) {}  // from a const ref
   X(std::string&& s) : s(std::move(s)) {}  // from an r-value reference
*/

  // ok - you made _s const, so this whole class is now not assignable
  const std::string s;

  // another way is to have a private member and a const accessor
  // you will then be able to assign an X to another X if you wish

/*    
  const std::string& value() const {
    return s;
  }

private:
  std::string s;
*/
}; 
票数 11
EN

Stack Overflow用户

发布于 2015-12-15 21:12:32

如果构造函数真的“接受一个字符串并且存储它为”,那么您的类当然需要包含一个std::string数据成员。指针只指向实际上不属于的其他字符串,更不用说“存储”了:

代码语言:javascript
复制
struct X
{
    explicit X(std::string s) : s_(std::move(s)) {}

    std::string s_;
};

注意,由于我们获得了字符串的所有权,所以我们最好按值取它,然后从构造函数参数中移开。

票数 7
EN

Stack Overflow用户

发布于 2015-12-15 21:16:37

在大多数情况下,您会希望按值进行复制。如果std::stringX之外被销毁,X将不知道它并导致不想要的行为。但是,如果我们想在不复制的情况下这样做,那么自然的做法可能是使用std::unique_ptr<std::string>并在上面使用std::move操作符:

代码语言:javascript
复制
class X {
public:
    std::unique_ptr<std::string> m_str;
    X(std::unique_ptr<std::string> str)
      : m_str(std::move(str)) { }
}

通过这样做,请注意原始std::unique_ptr将为空。数据的所有权已经转移。这样做的好处是,它保护数据,而不需要副本的开销。

或者,如果您仍然希望从外部世界访问它,则可以使用std::shared_ptr<std::string>,尽管在这种情况下必须小心。

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

https://stackoverflow.com/questions/34299446

复制
相关文章

相似问题

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