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

dsc*_*tti 9 c++ copy-constructor initializer-list

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

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();
}
Run Code Online (Sandbox Code Playgroud)

编辑:这里是 allocate() 成员函数,如果它有助于解释任何事情......storage数组已经被分配为与原始数组相同的长度,并由混合对象类型自己的复制赋值构造函数给出了深层副本,剩下的就是就是将这个PhaseSpace对象自己的POINTER类Hybrid对象设置到ARRAY类Hybrid对象中的适当位置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);
}

Run Code Online (Sandbox Code Playgroud)

这些注释来自实际代码,尽管我(也许显然)省略了原始结构定义中的大量细节。我的问题是复制构造函数是否需要如此详细的初始值设定项列表,或者是否有更优雅(且不易出错)的简写来制作这样的自定义复制构造函数。一旦我找到了这个问题的答案,我就会得到一些具有相同情况的更大的物体。

干杯!

eer*_*ika 7

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();
    }
};
Run Code Online (Sandbox Code Playgroud)