关于C++中构造函数(和多重继承)的一些基本问题?

ELL*_*BLE 4 c++ inheritance constructor class multiple-inheritance

(如果之前有人询问过,我很抱歉;搜索功能似乎已被破坏:结果区域完全是空白的,即使它表示在Chrome,FireFox和Safari中有几页结果...

所以,我只是在学习C++ ......而我正在阅读的这本书在解释构造函数方面做得非常糟糕,我能够掌握它们.到目前为止,我已经非常了解其他所有内容,但我无法弄清楚构造函数的语法实际上是如何工作的.

例如,我被告知以下将导致构造函数调用指定的超类的构造函数:

class something : something_else {
  something(int foo, double bar) : something_else(int foo) {}
};
Run Code Online (Sandbox Code Playgroud)

另一方面,在描述如何初始化const成员时,本书后面使用了相同的语法:

class something : something_else {
private:  const int constant_member;
public:   something(int foo, double bar) : constant_member(42) {}
};
Run Code Online (Sandbox Code Playgroud)

那么......呃......那到底是怎么回事?语法rv signature(param) : something_else(what);究竟意味着什么?我无法弄清楚它something_else(what)是什么,与周围的代码有关.它似乎具有多重含义; 我确信必须有一些它所对应的语言的基本元素,我只是无法弄清楚是什么.

编辑:另外,我应该提一下,what前面的例子中有时候是一个参数列表(所以something_else(what)看起来像一个函数签名)是非常令人困惑的......有时候是一个常量值表达式(所以something_else(what)看起来像一个函数调用).

现在,继续:多继承和构造函数怎么样?如何指定调用父类的构造函数以及默认调用哪些构造函数?我知道,默认情况下,以下两个是相同的......但是我不确定涉及多重继承时的等价物是什么:

class something : something_else {
//something(int foo, double bar) : something_else() {}
  something(int foo, double bar) {}
};
Run Code Online (Sandbox Code Playgroud)

任何帮助解决这些主题的人都会非常感激; 我不喜欢这种感觉,我没有理解基本的东西.我不喜欢它在所有.

编辑2:好的,截至目前的答案都非常有用.他们提出了这个问题的另一部分:"初始化列表"中的基类构造函数调用的参数如何与您定义的构造函数相关?他们必须匹配......是否必须有默认值?他们有多少匹配?换句话说,以下哪项是非法的:

class something_else {
  something_else(int foo, double bar = 0.0) {}
  something_else(double gaz) {}
};


class something : something_else {
  something(int foo, double bar)  : something_else(int foo, double bar) {}   };
class something : something_else {
  something(int foo)              : something_else(int foo, double bar) {}   };
class something : something_else {
  something(double bar, int foo)  : something_else(double gaz) {}   };
Run Code Online (Sandbox Code Playgroud)

Dav*_*eas 5

构造函数定义的语法是:

Type( parameter-list ) : initialization-list 
{
   constructor-body
};
Run Code Online (Sandbox Code Playgroud)

其中'initialization-list'是以逗号分隔的对base和/或成员属性的构造函数的调用列表.需要初始化没有默认构造函数,常量子对象和引用属性的任何子对象(基础或成员),并且在所有其他情况下应优先于构造函数块中的赋值.

struct base {
   base( int ) {};
};
struct base2 {
   base2( int ) {};
};
struct type : base, base2
{
   type( int x ) 
      : member2(x), 
        base2(5), 
        base(1), 
        member1(x*2) 
   { f(); }
   int member1;
   int member2;
};
Run Code Online (Sandbox Code Playgroud)

执行初始化列表的顺序在类声明中定义:基于它们的声明顺序,成员属性按声明的顺序排列.在上面的示例中,在f()构造函数体中执行之前,类将按以下顺序初始化其基类和属性:

  1. base(int)使用参数1 调用构造函数
  2. base2(int)使用参数5 调用构造函数
  3. member1用值初始化x*2
  4. member2用值初始化x

当您引入虚拟继承时,虚拟基础在虚拟继承层次结构的最派生类中初始化,因此它可以(或必须,如果没有默认构造函数)出现在该初始化列表中.在这种情况下,虚拟基础将在第一个子对象之前初始化,该子对象实际上从该基础继承.

class unrelated {};
class base {};
class vd1 : virtual base {};
class vd2 : virtual base {};
struct derived : unrelated, vd1, vd2 {
   derived() : unrelated(), base(), vd1(), vd2() {} // in actual order
};
Run Code Online (Sandbox Code Playgroud)

在编辑2

我想你不是在阅读答案中的细节.初始化列表中的元素是构造函数调用,而不是声明.如果合适,编译器将应用通常的转换规则.

struct base {
   base( int x, double y );
   explicit base( char x );
};
struct derived : base {
   derived() : base( 5, 1.3 ) {}
   derived( int x ) : base( x, x ) {} 
      // will convert x into a double and call base(int,double)
   derived( double d ) : base( 5 ) {} 
      // will convert 5 to char and call base(char)
// derived( base b ) {} // error, base has no default constructor
// derived( base b, int x ) : base( "Hi" ) {} 
      // error, no constructor of base takes a const char *
};
Run Code Online (Sandbox Code Playgroud)

  • @ elliot - Class的默认访问是私有的,而struct是公共的.这确实是他们之间唯一的区别. (3认同)
  • 看起来值得坚持的事实是,定义初始化列表的顺序无关紧要:它是控制初始化顺序的类定义.如果你自己的列表组织方式不同,好的编译器会发出警告,以便你可以检查. (3认同)
  • `class X {`相当于`struct X {private:`,反之亦然:`struct X {`相当于`class X {public:` (2认同)