我尝试将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容器中存储(复制)之后的每个实例应该仍然是线程安全的,所以我应该选择哪种情况?
答案 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::emplace
和piecewise_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}}