抱歉,标题有些愚蠢,我想不出一种简洁的方式来问我要问的问题。假设我有以下课程:
class Vector {
public:
std::array<int,3> data;
int& x = data[0];
int& y = data[1];
int& z = data[2];
int& operator[](const int& index){
return data[index];
}
};
并且我正在尝试遵循一个接口,该接口需要.x,.y,.z以及索引以及获取和设置数据元素的方式(不能引入getter和setter)。
x,y和z引用何时初始化?我注意到,它们不会在复制结构上重新初始化,但是在通过数据数组复制后手动重新初始化它们不会导致任何其他指令(好!)。会一直这样吗?
我主要是想了解这种方法的利弊。我试图避免传统与类型处理的结合,因为我试图编写高度符合标准的代码,而且我也希望不增加任何一种访问方法的开销。
答案 0 :(得分:7)
x,y和z引用何时初始化?
它们在构造期间被初始化。 由于您使用的是默认成员初始化,因此与您编写的相同:
Vector() : x(data[0]), y(data[1]), z(data[2]) {}
这些默认初始化将添加到每个构造函数,除非您在初始化列表中显式覆盖它们。分配不会(也不能)修改这些引用(不能重新引用)。
但是复制构造函数很奇怪。引用设置为指向其他实例,而不是新实例。糟糕!
在解决这个问题之后,似乎主要编译器无法优化此访问模式:使用.x
会经历额外的间接访问级别,即使编译器知道它始终指向{{1 }}。我不明白为什么编译器无法优化它。
但是,您是否考虑过翻转此模式并使用data[0]
访问名为operator[]
的成员变量等?即使看起来很多代码,只要操作员是内联的,它实际上会生成相同的代码,而不管访问模式如何,并且在构造过程中不会有开销或任何内存开销。
很高兴,我相信int x
可能是一个很好的方法:
operator[]
然后该课程的用户可以说以下内容
int& operator[](const int index){
return data[index];
}
答案 1 :(得分:7)
x,y和z引用何时初始化?
它们在data
之后的构造函数中按照在类中声明的顺序进行初始化。如果您未提供类成员初始化器,则它们只是语法糖,可用于在类成员初始化器列表中将该值用于初始化器。
这与复制构造函数不同。复制构造函数将使用旧类的引用来初始化新类中的每个引用,这意味着您的新对象具有对旧对象的引用。这不是您想要的。为了使其正常工作,您必须编写自己的副本构造函数,如
Vector(Vector const& vec) : data(vec.data), x(data[0]), y(data[1]), z(data[2]) {}
尽管我想为您提供一个不同的选择,但我不想这样做。您的类占用了数组空间和引用空间,从而使其超出了需要。您只需使用成员变量和自定义operator[]
就可以实现具有数组访问和成员访问的相同目标。例如,如果您使用
class Vector {
public:
int x = 0;
int y = 0;
int z = 0;
int& operator[](const int& index){
switch (index)
{
case 0: return x;
case 1: return y;
case 2: return z;
default: throw "some type of exception";
}
}
};
您无需完全考虑即可获得所需的复制和移动行为,并且还可以获得所需的阵列访问权限。您还从类中删除了引用的开销,这将使它的大小减小很多。例如on coliru,它将大小从40字节更改为12。
答案 2 :(得分:0)
作为数据成员的原始引用总是很难处理,尤其是与值语义结合使用时(由于提到了复制构造,您似乎想要这样做,并且引用无法重新初始化,请参见下文!)。
我建议改为使用std::reference_wrapper
。
手动重新初始化[参考]
您无法重新初始化(原始)引用!
关于数据成员的初始化顺序:非静态数据成员被初始化in the order in which they appear in the class definition。
我也希望不增加任何一种访问方法的开销。
如果您能够更改界面,建议改用成员函数:
struct Vector
{
std::array<int, 3> data;
int & x() { return data[0]; }
int & y() { return data[1]; }
int & z() { return data[2]; }
int & operator[](int idx) { return data[idx]; } // or data.at(idx)
// same fun as above for const ... and possibly volatile
}