我应该在成员变量中存储对映射的引用吗



我有以下类:

class State {
std::string name;
int id;
};
class Device {
private:
int stateID;
public:
const std::map<int, State> possibleStates;
Device(std::map<int, State> states) : possibleStates(states) {}
};

我希望尽可能提高记忆力。我的问题是,如果我将映射存储在Device类中作为引用(例如const std::map<int, State>& possibleStates),会有什么不同吗?如果我保持代码原样,那么每次调用Device构造函数时会创建映射的副本吗?

引用成员是允许的,但这是一件危险的事情。它需要引用类对象之外的另一个对象,其中一些其他代码确保所引用的对象在类成员可能再次使用的时间内一定存在。

如果你只更改了会员类型,并且有:

class Device {
private:
int stateID;
public:
const std::map<int, State>& possibleStates;
Device(std::map<int, State> states) : possibleStates(states) {}
};

那么这几乎肯定是错误的。该引用将绑定到构造函数的参数states,一旦构造函数主体完成,states的生存期就会结束,从而使possibleStates成为一个悬空引用!

如果您还更改了构造函数参数类型,那么它就有点可用了。。。

class Device {
private:
int stateID;
public:
const std::map<int, State>& possibleStates;
Device(const std::map<int, State>& states) : possibleStates(states) {}
};

但现在,创建Device对象的每一位代码都需要小心,并确保传入的map参数在需要的时候都会存在。该类的用户可能没有预料到这一点,即使它是已知的,也很容易意外出错,比如从未命名的临时std::map对象创建Device,该对象很快就会被销毁。

避免不必要拷贝的真正解决方案是:

#include <utility>
class Device {
private:
int stateID;
public:
const std::map<int, State> possibleStates;
Device(std::map<int, State> states) : possibleStates(std::move(states)) {}
};

std::movemap构造函数知道,此代码并不关心在states对象中保持一致的值。因此允许CCD_ 13的移动构造函数";偷窃;从states分配内存以创建possibleStates,而不是复制map内部细节和string内容的所有内存。这是有意义的,因为states是构造函数定义的本地,并且即将被销毁,之后甚至没有其他内容再次命名参数变量。

Device(std::map<int, State>);中的参数类型保留为对象类型而不是引用类型还允许创建Device的代码确定是否应该通过复制、移动或完全使用其他map构造函数来创建该参数对象。

您的代码将按原样生成映射的副本(包括值中状态的副本)。有一个引用会占用更少的内存,但其他东西会对映射负责。所有设备都会共享相同的可能状态图吗?

看起来更像是想要一个枚举来保存状态,以及一个映射来提供状态和名称之间的链接。

class Device {
public:
enum State {
init,
state_2,
state_c,
};
static const std::unordered_map<State, std::string> state_names;
Device() : stateID(State::init) {}
private:
State stateID;
};

不过,静态变量需要初始化。这通常是在.cpp文件中完成的,而不是在.h文件中,因为您只希望它在一个编译单元中初始化。

const std::unordered_map<Device::State, std::string> Device::state_names {
{State::init, "initialize"},
{State::state_2, "second state"},
{State::state_c, "another state"}
};

总之,它看起来像这样:

#include <unordered_map>
#include <string>
#include <iostream>
class Device {
public:
enum State {
init,
state_2,
state_c,
};
static const std::unordered_map<State, std::string> state_names;
Device() : stateID(State::init) {}
Device(State s) : stateID(s) {}
const std::string& current_state() const {
return state_names.at(stateID);
}
private:
State stateID;
};
const std::unordered_map<Device::State, std::string> Device::state_names {
{State::init, "initialize"},
{State::state_2, "second state"},
{State::state_c, "another state"}
};
int main(int /* argc */, char** /* argv */) {
Device d1;
Device d2(Device::State::state_c);
std::cout << "Device 1 state: " << d1.current_state() << 'n';
std::cout << "Device 2 state: " << d2.current_state() << 'n';
}

输出:

Device 1 state: initialize
Device 2 state: another state

Godbolt

现在,您只有一个编译时可能的状态集,一个状态到其名称的不可变映射,并且任何类都可以访问它。

最新更新