将包含类的类复制到std :: map

时间:2014-12-08 19:44:04

标签: c++ copy-constructor stdmap

我尝试将Section类的实例存储在std :: map容器中。应该可以从多个线程访问每个实例。所以该类必须是可复制的和线程安全的。下面是我的第一个方法,我需要一些解释这个代码的行为:

class SafePart{
  public:
    SafePart(){std::cout << "construct SafePart " << std::endl;};
   ~SafePart(){};
    SafePart(const SafePart &other){std::cout << "copy SafePart " << std::endl;};
    threadsafemethod(){std::lock_guard<std::mutex> lock(mtx); ...};
  private:

   // std::mutex mtx;
};

class Section {
  public:
    Section(){std::cout << "construct Section " << std::endl;};
   ~Section(){};
    somemethod(){sf.threadsafemethod();};
   // Section(const Section&){std::cout << "copy Section " << std::endl;};   
  private:
    SafePart sf;
};

int main() {
 std::map<int,Section> SecMap;  
 std::map<int,Section>::iterator it;
 Section s;
 SecMap.insert(std::make_pair(1,s));
 it=BoardMap.find(4);
 it->second.somemethod(); // is it still threadsafe?
 return 0;
}

case1 :如果我将此复制构造函数Section(const Section&){std::cout<< "copy Section " <<std::endl;};提供给Section类,则输出为:

construct SafePart   
construct Section 
construct SafePart 
copy Section 
construct SafePart 
copy Section 

/*conlusion:constructor is called on SafePart, when Section is copied to std::Map*/

Case2 :当Section类没有复制构造函数(将其留给编译器)时,输出为:

construct SafePart 
construct Section 
copy SafePart 
copy SafePart 

/*conlusion:copy constructor is called on SafePart, when Section is copied to    std::Map*/

在std :: map容器中存储(复制)之后的每个实例应该仍然是线程安全的,所以我应该选择哪种情况?

1 个答案:

答案 0 :(得分:1)

第一个代码的问题在于,Section复制构造函数并不告诉编译器使用哪个SafePart构造函数,因此编译器只使用默认构造函数。您必须使用初始化列表来告诉编译器使用哪个SafePart构造函数。在没有任何复制构造函数的第二个示例中,编译器为您创建一个,默认的一个副本逐个构造成员,这是99%的正确行为。

通常,除非确实需要它们,否则请避免编写构造函数,赋值运算符和析构函数。何时需要它们,在构造函数中使用初始化列表。

这是我在Safepart中添加了复制分配和移动操作的地方。移动操作对您的特定代码没有帮助,但它们通常是个好主意。另请注意,我已将所有这些标记为noexcept。也没有必要,但是一个好主意。

class SafePart{
  public: 
     SafePart(){std::cout << "def ctor SafePart " << std::endl;};
     ~SafePart(){};
     SafePart(const SafePart &other) noexcept
     {std::cout << "cpy ctor SafePart " << std::endl;};
     SafePart&operator=(const SafePart &other) noexcept
     {std::cout << "cpy asn SafePart " << std::endl;return *this;};
     SafePart(SafePart &&other) noexcept
     {std::cout << "mov ctor SafePart " << std::endl;};
     SafePart&operator=(SafePart &&other) noexcept
     {std::cout << "mov asn SafePart " << std::endl;return *this;};
};

以下是如何处理成员和父类。关键部分是初始化列表:sf(other.sf)和类似的。这会得到理想的行为。

class Section {
  public:
    Section (){std::cout << "def ctor SafePart " << std::endl;};
   ~Section (){std::cout << "dtor SafePart " << std::endl;};

    Section (const Section &other) noexcept 
    :sf(other.sf)
    {std::cout << "cpy ctor SafePart " << std::endl;};

    Section & operator=(const Section &other) noexcept
    {sf=other.sf; std::cout << "cpy asn SafePart " << std::endl;}; 

    Section (Section &&other) noexcept
    :sf(std::move(other.sf))
    {std::cout << "move ctor SafePart " << std::endl;};

    Section & operator=(const Section &&other) noexcept
    {sf=std::move(other.sf); std::cout << "mov asn SafePart " << std::endl;};

  private:
    SafePart sf;
};

我使用map::emplacepiecewise_construct以及两个forward_as_tuple来电调,使用int main() { std::map<int,Section> SecMap; std::map<int,Section>::iterator it; Section s; std::cout << "BREAKPOINT 1\n"; SecMap.emplace(std::piecewise_construct,std::forward_as_tuple(1),std::forward_as_tuple(s)); std::cout << "BREAKPOINT 2\n"; return 0; } 来调整您的主要内容,以便不执行不必要的副本。这导致它跳过副本。

def ctor SafePart 
def ctor Section 
BREAKPOINT 1
cpy ctor SafePart 
cpy ctor Section 
BREAKPOINT 2
dtor Section 
dtor Section

所以现在我得到了这个输出:http://coliru.stacked-crooked.com/a/219644edd8253da9

{{1}}