【问题标题】:Calling overloaded constructor from constructor initialisation list从构造函数初始化列表调用重载的构造函数
【发布时间】:2011-10-06 18:19:54
【问题描述】:

在下面的代码中,我的意图是根据传递给material 类对象的参数来调用kapopacity 类)的两个重载构造函数之一:

class opacity{
 private:
  int mode;
  double kap_const;
  double kappa_array[10][10];

 public:
  opacity(double constkap);  // picking the constructor sets the mode
  opacity(char* Datafile);
  double value(double T, double P); // will return a constant or interpolate
};

opacity::opacity(double constkap):mode(1){
  kap_const = constkap;
}

opacity::opacity(char* Datafile):mode(2){
  // read file into kappa_array...
}

class Matter {
 public:
  Matter(int i, double k, char* filename); // many more values are actually passed
  opacity kap;
  int x;  // dummy thing
  // more variables, call some functions
};

Matter::Matter(int i, double k, char * filename)
 :x(k>0? this->kap(x): this->kap(filename) ) {
  // ... rest of initialisation
 }

但这不起作用:

test.cpp: In constructor 'Matter::Matter(int, double, char*)':
test.cpp:32:21: error: no match for call to '(opacity) (void*&)'
test.cpp:32:42: error: no match for call to '(opacity) (char*&)'
test.cpp:32:44: error: no matching function for call to 'opacity::opacity()'
test.cpp:32:44: note: candidates are:
test.cpp:20:1: note: opacity::opacity(char*)
test.cpp:20:1: note:   candidate expects 1 argument, 0 provided
test.cpp:16:1: note: opacity::opacity(double)
test.cpp:16:1: note:   candidate expects 1 argument, 0 provided
test.cpp:4:7: note: opacity::opacity(const opacity&)
test.cpp:4:7: note:   candidate expects 1 argument, 0 provided

我尝试的第一件事,

Matter::Matter(int i, double k, char * filename)
 :kap(k>0? k: filename) {   // use k<0 as a flag to read from filename
  // ... rest of initialisation
}

也失败了,因为“三元运算符的结果总是必须是相同的类型”出于编译时的原因,正如similar question 中所指出的那样(尽管似乎没有解释)。

现在,不优雅的解决方案是基于 kap 构造函数应接收的参数重载 Matter 构造函数,但这是 (1) 非常不优雅,特别是因为 Matter 构造函数需要许多变量并执行许多操作(因此很多代码将被复制只是为了改变构造函数初始化列表的kap 部分),并且(2)如果有另一个与Matter 一起使用的类,这可能会失控有不同的构造函数:对于具有 N 个 c'tors 的 M 类,一个以 N^ M 个组合结束。 ..

有人有什么建议或解决方法吗?提前致谢!

【问题讨论】:

  • 我认为你必须向opacity 添加一个特殊的构造函数。要么接受一个 int 和文件名,要么一个默认构造函数,以便您可以在 Matters 构造函数的主体中分配它。
  • 为什么不将不透明度 kap 作为构造函数参数传递?您可以在调用构造函数之前使用三元运算符,然后创建一个不透明对象(可能在堆上)并在事务构造函数中传递一个指向它的指针。
  • @MooingDuck 啊,你是说 Johannes Schaub 写的吗?谢谢你的回答。
  • @John 如果我正确理解您的建议,有一个警告:“用户”(好吧,这只是我......)应该只使用他创建的 Matter 对象,并且不必单独创建opacity 实例——它只被Matter 使用。从我使用public 来看,这个意图可能并不清楚。

标签: c++ ternary-operator initializer-list constructor-overloading


【解决方案1】:

如果 opacity 有一个复制构造函数,您可以在初始化列表中完成此操作,避免使用默认构造函数,但以复制为代价:

  Matter::Matter(int i, double k, char * filename)
     :kap( ( 0 < k ) ? opacity(k) : opacity( filename ) ) { ... }

【讨论】:

  • 与 Johannes Schaub 的答案相同,只是更紧凑。谢谢。
  • @Labbedudl :相同——约翰内斯的回答使用默认初始化,然后是赋值,这个使用复制初始化。即,这个(理论上)效率更高,并且不需要 opacity 是默认可构造的。
  • @ildjarn:理论上效率更高,而使用 RVO,实际上效率更高。
  • @ildjarn 感谢您的澄清!我会选择这个答案,因为效率更高。
  • @Labbedudl,意识到它需要opacity 的正常工作的复制构造函数。如果opacity 的复制成本很高,那么它根本不会更有效率。不过,我怀疑这对你来说是个问题。
【解决方案2】:

您将不得不向opacity 添加一个默认构造函数(这可能会将模式设置为0 以指示无效模式)并在构造函数主体中分配给kap

Matter::Matter(int i, double k, char * filename) {
  if(k > 0)
    kap = opacity(k);
  else
    kap = opacity(filename);
}

参数k 是运行时值。不可能使类型和重载结果取决于运行时值。

【讨论】:

  • 谢谢,成功了!我正在想象一个可以只从初始化列表中调用opacity 构造函数...
【解决方案3】:

为了避免复制开销,假设你有一个 C++0x 编译器,你可以给 opacity 一个 move constructor 并让一个静态函数根据你的逻辑提供一个 opacity 的实例并初始化你的 kap 成员使用返回的临时opacity

您可能想要创建 kappa_array 一些指针,例如 auto_ptr&lt;double&gt;。尽管如果在紧密循环中使用此数据,与局部性和取消引用指针的成本相比,可移动性所节省的成本可能是值得怀疑的。

opacity& get_opacity(double k, char * filename) {
    if(k > 0)
        return opacity(k);
    else
        return opacity(filename);
}

Matter::Mater(int i, double k, char * filename)
    : kap(get_opacity(k, filename) {
   //...
}

opacity::opacity(opacity&& other)
    : mode(other.mode),
      kap_const(other.kap_const),
      kappa_array(std::move(kappa_array)) { }

请不要在这方面测试我,我自己对移动语义和右值引用还是很陌生...

【讨论】:

    【解决方案4】:

    您不能使用三元运算符来选择函数覆盖,因为运算符的结果只有一种类型。在不同的分支有不同类型的情况下,它们将被强制为结果类型,否则会出现编译时错误。见http://en.wikipedia.org/wiki/%3F:#Result_type

    真的不可能有任何其他方式。编译器需要在编译时知道类型,但是直到运行时才知道操作的结果。

    【讨论】:

    • 是的,我也看到了这一点,编译器显然必须在编译时知道。但是,这是为什么呢?
    • @Labbedudl,编译器在编译代码时会生成对单个函数的单个调用。它必须知道当时哪个函数签名匹配。如果您需要在两个不同的函数调用之间进行选择,则必须在两个不同的代码分支中明确说明它们。
    猜你喜欢
    • 1970-01-01
    • 2012-04-11
    • 2013-10-03
    • 2015-08-08
    • 2013-04-01
    • 1970-01-01
    • 2012-03-19
    • 2017-05-02
    • 2018-06-20
    相关资源
    最近更新 更多