如何初始化基类构造函数的向量

How to initialize a vector of base-class constructors?

本文关键字:向量 构造函数 基类 初始化      更新时间:2023-10-16

我有一个没有默认构造函数的base_class,我想定义它的向量版本(称为derived_class(。我知道我应该在我的 derived_class 构造函数中初始化 base_class 构造函数,但是下面的代码试图初始化每行大小为 dimbase_class(0,1) 的向量无法编译(它抱怨错误:"derived_class"的构造函数必须显式初始化没有默认构造函数的基类"base_class">(,除非我将默认构造函数(注释行(添加到base_class.我错过或误解了什么吗?有没有办法在不定义默认base_class构造函数的情况下使其工作?

#include <iostream>
#include <vector>
class base_class
{
   public:
      //base_class(){};
      base_class(double a, double b){a_=a; b_=b;}
   private:
      double a_, b_;
};
class derived_class : public base_class
{
   public:
      derived_class(int dim): vector_object(dim, base_class(0,1)){};
      std::vector<base_class> print_vector_object() {return vector_object;}
   private:
      std::vector<base_class> vector_object;
};

int main()
{
  int dim = 3;
  derived_class abc(3);
  std::cout << abc.print_vector_object().size() << std::endl;
  return 0;
}

更新:我知道在这个简单的情况下,我可以完全避免继承,但请假设我确实需要继承才能进行实际练习,谢谢。

UPDATE2:正如@vishal所暗示的,如果derived_class的构造函数写成

derived_class(int dim) : base_class(0,0), vector_object(dim, base_class(0,1)) {};

代码可以通过编译器。但是我仍然不明白为什么我必须以这种方式初始化......

好吧,由于您不想在基类中创建默认构造函数,因此您可以call base constructor by

derived_class(double a, double b, int dim) : base_class(a,b), vector_object(dim, base_class(0,1)) {};

您的vector_object是红鲱鱼,与问题无关,因为您可以使用以下代码段进行验证,这也将使编译器抱怨缺少默认构造函数base_class

class derived_class : public base_class
{
   public:
      derived_class(int dim) {} // error, tries to use `base_class` default constructor,
                                // but there is none...
};

必须提供默认构造函数或使用非默认构造函数。在derived_class的每个实例中,都有一个base_class子对象,并且必须以某种方式创建子对象。

误解的一个可能来源是继承层次结构中的对象是从上到下(从基到派生(初始化的。乍一看似乎不合逻辑,但是当derived_class构造函数运行时,base_class子对象必须已经存在。如果编译器无法提供此功能,则会出现错误。

因此,即使 derived_class 构造函数指定如何创建base_class子对象,子对象的实际创建也会在创建derived_class部件之前进行。

您正在询问以下内容:

不应该在我的代码中初始化base_class对象吗?

您的代码必须初始化它,但它不会。您不会在任何地方初始化它,并且默认初始化不起作用,因为基类没有默认构造函数。

有时,将继承视为一种特殊的组合形式会有所帮助(确实有一些惊人的相似之处(。以下代码段与您发布的代码存在相同的问题:

class base_class
{
   public:
      //base_class(){};
      base_class(double a, double b){a_=a; b_=b;}
   private:
      double a_, b_;
};
class derived_class // <--- no inheritance
{
   public:
      base_class my_base; // <--- member variable
      derived_class(int dim) {};
};

你还会认为derived_class初始化base_class对象吗?


另一个可能的误解来源是上述红鲱鱼。你是说:

只是不明白为什么我不能在向量中初始化它(...

因为向量成员变量与base_class子对象无关,也与我另一个示例中的公共base_class成员变量无关。向量成员变量和其他任何东西之间没有神奇的关系。

再次使用原始代码段,可以在内存中按如下方式描绘一个完整的derived_class对象:

+-------------------+
| +---------------+ |
| |     double    | | <--- `base_class` sub-object
| |     double    | |        
| +---------------+ |
+-------------------+              +--------+--------+--------+....
|     std::vector ---------------> | double | double | double |
+-------------------+              | double | double | double |
                                   +--------+--------+--------+....
                                        ^
                                        |
                                        |
                                  a lot of other
                                `base_class` objects 

向量管理的base_class对象与base_class子对象完全无关,该子对象的存在归功于类继承。

(该图有点过于简化,因为std::vector通常还存储一些内部簿记数据,但这与本次讨论无关。


但是,无论如何,您的代码都不会为继承提供令人信服的理由。那你当初为什么要继承呢?你不妨这样做:

#include <iostream>
#include <vector>
class base_class
{
   public:
      //base_class(){};
      base_class(double a, double b){a_=a; b_=b;}
   private:
      double a_, b_;
};
class derived_class // <--- no more inheritance (and thus wrong class name)
{
   public:
      derived_class(int dim) : vector_object(dim, base_class(0,1)){};
      std::vector<base_class> print_vector_object() {return vector_object;}
   private:
      std::vector<base_class> vector_object;
};

int main()
{
  int dim = 3;
  derived_class abc(3);
  std::cout << abc.print_vector_object().size() << std::endl;
  return 0;
}

你确定derived_class对象是一个base_class对象,并且包含base_class对象的vector吗?

我假设,您实际上只需要base_class对象的vector。如果这是真的,请不要从base_class中得出derived_class。实际上,这就是编译器消息的来源。它告诉你,你必须初始化derived_class对象的base_class方面。

所以这是我给你的解决方案(请注意,代码未经测试,可能包含一些错误(:

#include <iostream>
#include <vector>
class base_class
{
   public:
      // c++11 allows you to explicitly delete the default constructor.
      base_class() = delete;
      base_class(double a, double b):
          a_{a}, b_{b} // <- prefer initializers over constructor body
      {};
   private:
      double a_;
      double b_;
};
class derived_class // <- this name is wrong now of course; change it.
{
   public:
      derived_class(int dim):
          // better initialize doubles with float syntax, not int.
          vector_object{dim, base_class{0.0, 1.0}}
      {};
      // Note: this will copy the whole vector on return.
      // are you sure, that is what you really want?
      auto print_vector_object() -> std::vector<base_class> {
          return vector_object;
      };
   private:
      std::vector<base_class> vector_object;
};

int main(int, char**)
{
  // int dim = 3; <- this is useless, you never use dim.
  auto abc = derived_class{3};
  std::cout << abc.print_vector_object().size() << std::endl;
  return 0;
}

注意:我的代码应该是 C++11。

我稍微重写了你的类

#include <iostream>
#include <vector>
class base_class {
private:
    double a_, b_;
public:
    //base_class(){};
    base_class(double a, double b ) : a_(a), b_(b) {}
    virtual ~base_class();
};
class derived_class : public base_class {
private:
    std::vector<base_class> vector_object;
public:
    explicit derived_class( int dim ) : base_class( 0, 1 ) {
        vector_object.push_back( static_cast<base_class>( *this ) );
    }
    ~derived_class();
    std::vector<base_class> get_vector_object() const { return vector_object; }
};
int main() {
    int dim = 3;
    derived_class abc(3);
    std::cout << abc.get_vector_object().size() << std::endl;
    return 0;
}

这将正确构建和编译。我做了一些更改:我在适用的情况下使用了构造函数成员初始值设定项列表,我已将虚拟析构函数添加到基类;任何时候你从基类继承都应该有一个虚拟析构函数,我将所有私有成员变量移到类的顶部而不是底部,我用 explicit 关键字作为 derived_class 构造函数的前缀,因为你只有 1 个参数传入它,我已经更改了derived_class构造函数以在其成员的初始化列表中使用 base_class 构造函数,并在使用 push_back(( 方法并将取消引用的 this 指针静态强制转换为 base_class 类型的构造函数,并且我还将您的 print_vector_object 方法更改为 Just get_vector_object,因为它不打印任何内容,只返回类中的成员向量。我还将此方法声明为 const,这样它就不会更改类,因为您只是检索它。我唯一的问题是derived_class中的int参数有什么作用?它不在任何地方使用,并且您没有成员变量来存储它。

派生类的这一部分位于此处

公共: derived_class(int dim(: vector_object(dim, base_class(0,1(({};

你声明你的构造函数对我来说没有任何意义。

您似乎正在尝试使用成员初始化列表来填充向量成员变量,方法是传入派生类的传入参数并使用值 {0,1} 将构造函数调用到基类。这不会像您期望的那样工作。您首先必须构造基类对象,该对象是此派生类的子对象,而不是在构造函数的函数范围内,然后可以填充成员向量。

拥有的内容将导致问题,因为您没有提供可用于尝试构造派生类型的默认构造函数。因为从我可以收集到的关于您展示的内容以及您正在尝试执行的操作的内容来看,您似乎希望在调用派生构造函数时将base_class初始化为 {0,1};如果是这种情况,您可以在尝试构造派生类型时,只需调用base_class的已实现构造函数,并在其构造函数中传递 {0,1}。这将允许正确建造derived_class,因为base_class已经建造完毕。现在,您可以开始构造派生类型,看起来您希望将构造的base_class保存到派生类型向量成员变量中。这就是为什么我在构造函数中而不是在初始化列表中执行此操作的原因,并且我push_back派生类型的实例,该实例使用取消引用的 this 指针静态强制转换为基类型。此外,当我在您尝试打印其大小的主函数中设置断点并查看derived_class的成员变量时,它填充了 1 个值为 a = 0 和 b = 1 的对象。为了能够打印这些变量,您必须添加公共获取方法来返回每个变量。此外,如果派生类中的 int 参数不需要,则可以将其与显式关键字一起删除。您还可以为派生类型实现第二个构造函数,该构造函数像基一样接受两个双精度值,并将这些参数从 derived_class 构造函数传递到base_class构造函数,如下所示:

public:
    derived_class( double a, double b ) : base_class( a, b ) {
        vector_object.push_back( static_cast<base_class>( *this ) );
    }

我将演示一个基类和两个派生类的简单示例,其中第一个实例必须在构造时设置值,而第二个实例必须使用自己的构造函数,其中基类中没有定义默认构造函数。

class Base {
private:
    double x_, y_, z_;
public:
    Base( double x, double y, double z );
    virtual ~Base();  
};
Base::Base() : 
x_( x ), y_( x ), z_( z ) {
}
Base::~Base() {
}
class DerivedA : public Base {
private:
    int value_;
public:
    explicit DerivedA( int value );
    ~DerivedA();
};
// In This Case A Must Set The Values In The Constructor Itself: I'll Just
// Set Them To (0, 0, 0) To Keep It Simple
DerivedA::DerivedA( int value ) :
Base( 0, 0, 0 ),
value_( value ) {
}
DerivedA::~DerivedA() {
}
class DerivedB : public Base {
private:
    int value_;
public:
    DerivedB( int value, double x, double y, double z );
    ~DerivedB();
};
// In This Case B Doesn't Have To Know What Is Needed To Construct Base
// Since Its Constructor Expects Values From Its Caller And They Can
// Be Passed Off To The Base Class Constructor
DerivedB::DerivedB( int value, double x, double y, double z ) :
Base( x, y, z ),
value_( value ) {
}
DerivedB::~DerivedB() {
}    

让我知道这是否对您有帮助!