【发布时间】:2022-01-08 12:41:34
【问题描述】:
我正在编写一个模板函数,它接受一个自定义类(可以是任何类或原始类型)作为模板参数,然后从输入流中读取一些数据(该类型的),然后将其存储为无序映射类似于这个:
std::unordered_map<CustomClass, std::vector<CustomClass>> map;
我已经实现了一个自定义类来测试行为。我重载了 std::hash 以便此类可以作为键存储在无序映射中,并重载所有运算符和构造函数,这样无论何时调用它们,我都会在控制台中收到一条消息(例如,当复制构造函数是调用时,我收到一条消息“复制构造函数 [..data...]”)
编辑:根据 cmets 的要求,这里是自定义类的定义和实现(请注意:这里的类只是一个占位符,所以我们可以讨论这个问题背后的总体思路。我是很清楚这是愚蠢的,不应该这样实现。运算符>>和
class CustomClass {
public:
CustomClass(int a=0) {
std::cout << "default constructor" << std::endl;
m_data = a;
}
CustomClass(const CustomClass& other) {
std::cout << "copy constructor " ;//<< std::endl;
m_data = other.m_data;
std::cout << "[" << m_data << "]" << std::endl;
}
CustomClass(CustomClass&& other) {
std::cout << "move cosntructor" << std::endl;
m_data = other.m_data;
}
CustomClass& operator=(const CustomClass& other) {
std::cout << "copy assignment operator" << std::endl;
if(this != &other){
m_data = other.m_data;
}
return *this;
}
CustomClass& operator=(CustomClass&& other) {
std::cout << "move assignment operator" << std::endl;
if(this != &other){
m_data = other.m_data;
}
return *this;
}
~CustomClass() {
std::cout << "destructor" << std::endl;
}
int m_data;
};
现在我的问题是:是否可以从输入流中读取数据并在不需要复制构造函数调用的地方就地构造它?
部分代码示例:
CustomClass x1; // default constructor call
CustomClass x2; // default constructor call
std::cout << "----" << std::endl;
std::cin >> x1 >> x2; // my input
std::cout << "----" << std::endl;
map[x1].emplace_back(x2); // 2 copy constructor calls
std::cout << "----" << std::endl;
std::cout << map[x1][0] << std::endl; // operator== call
std::cout << "----" << std::endl;
这是该代码的示例输出:
default constructor
default constructor
----
[1]
[2]
----
copy constructor [1]
copy constructor [2]
----
operator ==
[2]
----
destructor
destructor
destructor
destructor
我希望这个类的每个对象都只构造一次。
是否可以避免这些复制构造函数?如果不是两者,那么至少是在 emplace_back() 调用期间调用的那个?是否可以在向量中准确地在内存中需要的位置构造对象,但这种调用适用于每种类型?
如果我需要进一步详细说明我的问题,请在 cmets 中告诉我,我很乐意这样做
【问题讨论】:
-
假设这是 C++11 或更高版本,看起来你正在破坏the rule of five。你熟悉move semantics吗?
-
@Brian 我正在使用 C++17。我也重载了 move 构造函数和 move = 运算符(但是这里没有调用它们)。我对移动语义很熟悉,但不是很深入。我知道在播放指针时应该使用移动语义,但是我的代码并非如此,CustomClass 只存储 3 个整数变量。您愿意详细说明我在这里做错了什么吗?
-
您可以调用
resize()并使用back()从流中读取。 -
再看一遍,我以为我看到的问题不存在。我认为唯一的问题是您没有传达
x2可以在map[x1].emplace_back(x2);之后过期,因此需要制作一个副本以便与emplace_back 例外的右值引用绑定。您可以使用map[x1].emplace_back(std::move(x2));来解决这个问题。另见What is std::move(), and when should it be used? -
@TheMemeMachine 我认为应该有四个析构函数调用:一个用于
x1,一个用于x2,一个用于映射中的键,一个用于映射中的值。移出的对象仍然调用它们的析构函数。我不确定为什么你的程序在使用 MSVC 编译时会崩溃,因为它同时适用于 GCC 和 clang。
标签: c++ input constructor copy-constructor