C++复制构造函数:我必须拼写出初始值设定项列表中的所有成员变量吗



我有一些非常复杂的对象。它们包含其他对象的成员变量。我理解复制构造函数级联的美妙之处,因此默认的复制构造函数通常可以工作。但是,最经常破坏默认复制构造函数的情况(对象包含一些成员变量,这些变量是指向其他成员变量的指针(仍然适用于我所构建的许多内容。下面是我的一个对象、它的构造函数和我写的复制构造函数的例子:

struct PhaseSpace {
PhaseSpace();
private:
std::string file_name;              ///< Name of the file from which these coordinates (and
///<   perhaps velocities) derived.  Empty string indicates
///<   no file.
int atom_count;                     ///< The number of atoms in the system
UnitCellType unit_cell;             ///< The type of unit cell
Hybrid<double> x_coordinates;       ///< Cartesian X coordinates of all particles
Hybrid<double> y_coordinates;       ///< Cartesian Y coordinates of all particles
Hybrid<double> z_coordinates;       ///< Cartesian Z coordinates of all particles
Hybrid<double> box_space_transform; ///< Matrix to transform coordinates into box space (3 x 3)
Hybrid<double> inverse_transform;   ///< Matrix to transform coordinates into real space (3 x 3)
Hybrid<double> box_dimensions;      ///< Three lengths and three angles defining the box (lengths
///<   are given in Angstroms, angles in radians)
Hybrid<double> x_velocities;        ///< Cartesian X velocities of all particles
Hybrid<double> y_velocities;        ///< Cartesian Y velocities of all particles
Hybrid<double> z_velocities;        ///< Cartesian Z velocities of all particles
Hybrid<double> x_forces;            ///< Cartesian X forces acting on all particles
Hybrid<double> y_forces;            ///< Cartesian Y forces acting on all particles
Hybrid<double> z_forces;            ///< Cartesian Z forces acting on all particles
Hybrid<double> x_prior_coordinates; ///< Previous step Cartesian X coordinates of all particles
Hybrid<double> y_prior_coordinates; ///< Previous step Cartesian Y coordinates of all particles
Hybrid<double> z_prior_coordinates; ///< Previous step Cartesian Z coordinates of all particles
/// All of the above Hybrid objects are pointers into this single large array, segmented to hold
/// each type of information with zero-padding to accommodate the HPC warp size.
Hybrid<double> storage;
/// brief Allocate space for the object, based on a known number of atoms
void allocate();
};
//-------------------------------------------------------------------------------------------------
PhaseSpace::PhaseSpace() :
file_name{std::string("")},
atom_count{0},
unit_cell{UnitCellType::NONE},
x_coordinates{HybridKind::POINTER, "x_coordinates"},
y_coordinates{HybridKind::POINTER, "y_coordinates"},
z_coordinates{HybridKind::POINTER, "z_coordinates"},
box_space_transform{HybridKind::POINTER, "box_transform"},
inverse_transform{HybridKind::POINTER, "inv_transform"},
box_dimensions{HybridKind::POINTER, "box_dimensions"},
x_velocities{HybridKind::POINTER, "x_velocities"},
y_velocities{HybridKind::POINTER, "y_velocities"},
z_velocities{HybridKind::POINTER, "z_velocities"},
x_forces{HybridKind::POINTER, "x_forces"},
y_forces{HybridKind::POINTER, "y_forces"},
z_forces{HybridKind::POINTER, "z_forces"},
x_prior_coordinates{HybridKind::POINTER, "x_prior_coords"},
y_prior_coordinates{HybridKind::POINTER, "y_prior_coords"},
z_prior_coordinates{HybridKind::POINTER, "z_prior_coords"},
storage{HybridKind::ARRAY, "phase_space_data"}
{}
//-------------------------------------------------------------------------------------------------
PhaseSpace::PhaseSpace(const PhaseSpace &original) :
file_name{original.file_name},
atom_count{original.atom_count},
unit_cell{original.unit_cell},
x_coordinates{original.x_coordinates},
y_coordinates{original.y_coordinates},
z_coordinates{original.z_coordinates},
box_space_transform{original.box_space_transform},
inverse_transform{original.inverse_transform},
box_dimensions{original.box_dimensions},
x_velocities{original.x_velocities},
y_velocities{original.y_velocities},
z_velocities{original.z_velocities},
x_forces{original.x_forces},
y_forces{original.y_forces},
z_forces{original.z_forces},
x_prior_coordinates{original.x_prior_coordinates},
y_prior_coordinates{original.y_prior_coordinates},
z_prior_coordinates{original.z_prior_coordinates},
storage{original.storage}
{
// Set the POINTER-kind Hybrids in the new object appropriately.  Even the resize() operation
// inherent to "allocate" will pass by with little more than a check that the length of the data
// storage array is already what it should be.
allocate();
}

EDIT:这是allocate((成员函数,如果它有助于解释任何事情的话。。。由于storage数组已经被分配到与原始数组相同的长度,并由混合对象类型自己的副本分配构造函数赋予深度副本,剩下的就是将该PhaseSpace对象自己的POINTER类型的混合对象设置到array类型混合对象storage中的适当位置。

//-------------------------------------------------------------------------------------------------
void PhaseSpace::allocate() {
const int padded_atom_count  = roundUp(atom_count, warp_size_int);
const int padded_matrix_size = roundUp(9, warp_size_int);
storage.resize((15 * padded_atom_count) + (3 * padded_matrix_size));
x_coordinates.setPointer(&storage,                            0, atom_count);
y_coordinates.setPointer(&storage,            padded_atom_count, atom_count);
z_coordinates.setPointer(&storage,        2 * padded_atom_count, atom_count);
box_space_transform.setPointer(&storage,  3 * padded_atom_count, 9);
inverse_transform.setPointer(&storage,   (3 * padded_atom_count) +      padded_matrix_size, 9);
box_dimensions.setPointer(&storage,      (3 * padded_atom_count) + (2 * padded_matrix_size), 6);
const int thus_far = (3 * padded_atom_count) + (3 * padded_matrix_size);
x_velocities.setPointer(&storage,        thus_far,                           atom_count);
y_velocities.setPointer(&storage,        thus_far +      padded_atom_count,  atom_count);
z_velocities.setPointer(&storage,        thus_far + (2 * padded_atom_count), atom_count);
x_forces.setPointer(&storage,            thus_far + (3 * padded_atom_count), atom_count);
y_forces.setPointer(&storage,            thus_far + (4 * padded_atom_count), atom_count);
z_forces.setPointer(&storage,            thus_far + (5 * padded_atom_count), atom_count);
x_prior_coordinates.setPointer(&storage, thus_far + (6 * padded_atom_count), atom_count);
y_prior_coordinates.setPointer(&storage, thus_far + (7 * padded_atom_count), atom_count);
z_prior_coordinates.setPointer(&storage, thus_far + (8 * padded_atom_count), atom_count);
}

注释来自实际的代码,尽管我(也许很明显(在原始结构定义中省略了很多细节。我的问题是复制构造函数是否需要这样一个详细的初始值设定项列表,或者是否有一个更优雅(而且不容易出错(的简写来制作这样的自定义复制构造函数。一旦我对这个问题有了答案,我就得到了一些更大的物体,它们也处于同样的情况。

干杯!

C++复制构造函数:我必须拼写出初始值设定项列表中的所有成员变量吗?

是的,如果你写了一个用户定义的复制构造函数,那么你必须为每个子对象写一个初始化器-除非你想默认初始化它们,在这种情况下你不需要任何初始化器-或者如果你可以使用默认的成员初始化器。

对象包含一些成员变量,这些变量是指向其其他成员变量的指针(

这是一种尽可能避免的设计。这不仅迫使您定义自定义的复制和移动赋值运算符和构造函数,而且通常效率不高。

但是,如果出于某种原因需要,或者出于任何其他原因需要自定义特殊成员函数,则可以通过将正常复制的部分组合到一个单独的伪类中来实现干净的代码。这样,用户定义的构造函数只有一个子对象需要初始化。

像这样:

struct just_data {
// many members... details don't matter
R just_data_1;
S just_data_2;
T just_data_3;
U just_data_4;
V just_data_5;
};
struct fancy_copying : just_data
{
fancy_copying(const fancy_copying& other)
: just_data(other.just_data)
{
do_the_fancy();
}
};

好吧,不,在构造函数的初始化程序列表中列出所有成员并不是必不可少的。但是,如果你不这样做,你需要接受并以某种方式减轻后果。

某些成员的首字母缩写是强制性的。例如,在创建引用时,有必要对其进行初始化(以使其正确地引用某些内容(。我想你并不是想让这些成员置身事外。

在构造函数初始化器列表中没有显式初始化的任何成员都将被默认初始化。

对于基本类型(例如intdouble、原始指针(,如果对象是全局对象,则默认初始化意味着零初始化,否则意味着"0";留下未初始化的";因此访问它的值会产生未定义的行为。所以,如果这样的成员是未初始化的,那么你唯一能做的就是分配它的值。执行任何访问其原始(未初始化(值的操作(递增、向其添加值、相乘、复制等(都会产生未定义的行为。

即使是基本的搜索也会找到各种解释,解释为什么(通常(避免引入未定义的行为是可取的。

对于struct/class类型,默认初始化意味着调用一个不接受任何参数的构造函数。如果该成员没有这样的构造函数,则需要显式初始化该成员(列出初始化该成员或调用它实际拥有的构造函数(。推理是递归的(取决于每个成员的成员(。

实际上,如果在构造函数中保留未初始化的类成员(与默认构造的成员不同(是有意义的,那么您可能需要问为什么该类有该成员。如果您想将成员的初始化延迟到对象构造之后,您需要质疑为什么不能延迟创建该类的实例。

最新更新