C++ STL容器和原地构造

8
请考虑以下内容:
class CMyClass
{
public:
  CMyClass()
  {
    printf( "Constructor\n" );
  }
  CMyClass( const CMyClass& )
  {
    printf( "Copy constructor\n" );
  }
};

int main()
{
  std::list<CMyClass> listMyClass;

  listMyClass.resize( 1 );

  return 0;
}

它会产生以下输出:
构造函数
拷贝构造函数
现在我的问题是:如何避免使用拷贝构造函数?或者换个方式说:如何在STL容器内创建对象而不必进行不必要的拷贝操作。是否有一种方法可以使用默认构造函数进行“就地”构建?
更新 - 到目前为止的答案:
1.无法实现
2.使用指针或智能指针代替。
对于我的应用程序来说,智能指针有些过度。但我真的很想知道为什么这不能做到。这似乎是一个显而易见的事情。还有其他想法吗?如果有效,我甚至会接受一个恶劣的hack...
解决方案:
我认为我刚刚从这里所有的评论和回答中找到了一个解决方案。解决方案是构造一个空对象并将其保留下来,以便以后仅用于制作干净的副本。然后,您可以使用其中一个采用引用的方法(如push_back或insert)。这仍然会为插入的每个新对象调用拷贝构造函数,但至少不是默认构造函数和拷贝构造函数两者都调用:
int main()
{
  CMyClass Empty;

  std::list<CMyClass> listMyClass;

  for ( int c=0; c<10; ++c )
  {
    listMyClass.push_back( Empty );
  }

  return 0;
}

这似乎是一个显而易见的事情,但从功能的角度来看,并不重要,因为几个算法(排序,删除等)也使用了复制,所以准备好被复制吧 ;) (参见:http://www.devx.com/tips/Tip/13606) - stefaanv
我想做的就是在列表中构造一个新对象。STL所做的是首先构造一个临时对象,然后使用复制构造函数复制临时对象。这种复制是不必要的浪费时间。mylist.resize(10)的另一种实现方式可以调用默认构造函数10次。它所做的是先调用默认构造函数一次,然后再调用复制构造函数10次。对于10个对象来说,这是一个小开销(10%),但如果您必须逐个添加对象,则开销会增加一倍(100%)。 - Barnett
你可能希望标准容器的行为是这样的,但它们并不是。在某些情况下,复制构造函数的调用可能会被编译器省略,但如果你不想依赖于它,为什么不像大家建议的那样使用指针呢? - anon
在我看来,这似乎是微小的优化。你确定那里有问题吗?此外,请注意,在大多数容器中,您不需要调整大小以添加新元素,而是在当前容器的末尾使用'push_back'。 - David Rodríguez - dribeas
该列表被用作数据流的FIFO缓冲区,因此优化是合理的。是的,在代码中使用了push_back() - 我只是在这里使用resize()作为一个例子,我认为它可以从具有不总是执行复制操作的实现中受益。 - Barnett
显示剩余4条评论
7个回答

8

按设计,所有 C++ 标准库容器都存储副本。因此,如果您希望在容器中存储值,则无法避免对复制构造函数的调用 - 唯一的出路是存储指针。如果您想减少复制的开销,请考虑使用引用计数。


2

抱歉,目前不支持std :: list和类似的容器。(但是如果您确实需要此功能,可以编写自己略有不同的容器,并仍然遵循STL接口的其余部分。)

但是,您无需使用默认构造函数:

std::list<CMyClass> listMyClass;
listMyClass.resize(1, obj_to_copy_from);

例如:
std::list<CMyClass> listMyClass;
listMyClass.resize(1, CMyClass(use, specific, ctor));

Resize的样子如下:

void list<T>::resize(size_type new_size, T copy_from = T());

默认情况下,它使用默认构造函数创建一个新对象。

1
使用指针
std::list<CMyClass*> listMyClass;

2
更好的做法是使用智能指针,自动创建/销毁相关对象。 - user231967
2
不要使用auto_ptr,否则会导致糟糕的后果。 - Michael Anderson

1

如果vector在调整大小时允许使用默认构造函数初始化新元素,那么这将非常有用,因为可以提高性能,但是目前不支持此功能。如果您确实需要这个功能,请实现自定义容器 - 没有其他方法。


0

你可以在C11中实现,在旧版本中非常简单,可以改进

向量

myclas: public vector<A>
{
    push_back(Aconstructor_parameters)
    {
       new(_Mylast++) A(Aconstructor_parameters);
    }
};

在使用时,请确保已经使用reserve()来分配内存空间


0

0

使用指针或智能指针(boost::shared_ptr而不是auto_ptr)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接