代码之家  ›  专栏  ›  技术社区  ›  gct

使用C++子类实例作为默认参数?

  •  2
  • gct  · 技术社区  · 15 年前

    所以我定义了几个类:

    class StatLogger {
    public:
      StatLogger();
     ~StatLogger();
    
      bool open(<parameters>);
    
    private:
      <minutiae>
    };
    

    以及子类,该子类从它派生以实现空对象模式(未打开的是它自己的空对象)

    class NullStatLogger : public StatLogger {
    public:
       NullStatLogger() : StatLogger() {}
    };
    

    然后我有第三个类,我想在它的构造函数中使用一个可选的logger实例:

    class ThirdClass {
    public:
      ThirdClass(StatLogger& logger=NullStatLogger());
    };
    

    我的问题是当我按上面的方法做的时候,我会得到:

    错误:参数的默认参数 类型为“statlogger&has “nullstatlogger”

    如果我在定义中使用显式转换,我会得到:

    错误:没有用于调用的匹配函数 到 –statlogger::statlogger(nullstatlogger)

    抱怨没有来自nullstatlogger的构造函数,即使它是子类。我在这里做了什么,C++中允许这样做吗?

    5 回复  |  直到 13 年前
        1
  •  5
  •   Jerry Coffin    15 年前

    如果你想用遗传和多态性, ThirdClass 需要使用指针或引用 StatLogger 对象,而不是实际对象。同样,在这种情况下,你几乎肯定需要 StatLogger::~StatLogger() 事实上的。

    例如,修改如下,代码应编译干净:

    class StatLogger {
    public:
      StatLogger();
      virtual ~StatLogger();
    
    //  bool open(<parameters>);
    
    private:
    //  <minutiae>
    };
    
    class NullStatLogger : public StatLogger {
    public:
       NullStatLogger() : StatLogger() {}
    };
    
    class ThirdClass {
        StatLogger *log;
    public:
      ThirdClass(StatLogger *logger=new NullStatLogger()) : log(logger) {}
    };
    

    编辑:如果您喜欢引用,代码如下所示:

    class StatLogger {
    public:
      StatLogger();
      virtual ~StatLogger();
    
    //  bool open(<parameters>);
    
    private:
    //  <minutiae>
    };
    
    class NullStatLogger : public StatLogger {
    public:
       NullStatLogger() : StatLogger() {}
    };
    
    class ThirdClass {
        StatLogger &log;
    public:
      ThirdClass(StatLogger &logger=*new NullStatLogger()) : log(logger) {}
    };
    
        2
  •  3
  •   Community CDub    8 年前

    根据年的讨论 Jerry's answer ,如果不使用默认变量来简化问题:

    class ThirdClass
    {
    
        StatLogger log;
    
        public:
    
            ThirdClass() : log(NullLogger()) {}
            ThirdClass(const StatLogger& logger) : log(logger) {}
    };
    
        3
  •  2
  •   David Rodríguez - dribeas    15 年前

    使用派生实例作为基引用的默认参数没有问题。

    现在,您不能将非常量引用绑定到临时(rvalue),这可能是编译器抱怨您的代码的原因之一,但我希望得到更好的诊断消息(不能将临时绑定到引用或类似的东西)。

    这个简单的测试可以完美地编译:

    class base {};
    class derived : public base {};
    void f( base const & b = derived() ) {} // note: const &
    int main() {
       f();
    }
    

    如果函数必须修改接收的参数,请考虑重构指针并提供默认的空值(不是默认的动态分配对象)。

    void f( base * b = 0) {
       if (b) b->log( "something" );
    }
    

    仅当您希望保留非常量引用接口并同时提供默认实例时,才必须提供静态实例,但我建议您不要这样做:

    namespace detail {
       derived d;
       // or:
       derived & null_logger() {
          static derived log;
          return log;
       }
    }
    void f( base & b = detail::d ) {}
    // or:
    void g( base & b = detail::default_value() ) {}
    
        4
  •  1
  •   Ricardo Ferreira    15 年前

    对于一个默认值我相信你必须提供一个默认值…

    ThirdClass(StatLogger *logger = NULL)
    

    例如

        5
  •  0
  •   penelope    13 年前

    呃,我知道这是一个很难回答的问题,但我有一个完全相同的问题,在阅读了所有建议的答案和评论之后,我想出了一个稍微不同的解决方案。

    我认为它也可能只适用于这里提出的问题实例,所以这里是:

    把一个 singleton 对象类型!

    对我来说,这是相当优雅和排序。很快,singleton是一个不能随意构造的对象,因为在任何时候都只能存在一个实例。(或者,在第一次使用之前可能有0个实例,因为您可以推迟初始化)。当然,您只能将singleton功能添加到派生类中,而父类的所有其他实例(和派生)都可以正常初始化和创建。但是,如果 NullStatLogger 就像我的情况一样,只是 笨蛋 类,它不在外部存储任何数据,并且根据instance/init参数没有不同的行为,singleton类非常适合。

    这里有一个简短的代码被剪断 nullstatlogger公司 一个单子,以及在 ThirdClass :

    class NullStatLogger : public StatLogger {
    private:
       NullStatLogger() : StatLogger() {}
       static NullStatLogger *_instance;
    public:       
       static NullStatLogger &instance();
    };
    
    NullStatLogger::_instance = 0;
    
    NullStatLogger &NullStatLogger:instance() {
        if (_instance == 0)
            _instance = new NullStatLogger(); // constructor private, this is
                                               // the only place you can call it
        return *_instance;                     // the same instance always returned
    }
    
    
    class ThirdClass {
    public:
        ThirdClass(StatLogger& logger=NullStatLogger::instance());
    };
    

    我知道这肯定不会帮助任何人提出问题,但希望它有助于其他人。