所以我这样定义了几个类:
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&amp;'类型有类型 “NullStatLogger”
如果我在定义中加上一个明确的强制转换,我会得到:
错误:没有匹配的呼叫功能 至 “StatLogger :: StatLogger(NullStatLogger)
抱怨NullStatLogger中没有构造函数,即使它是子类。我在这里做错了什么,这在C ++中是允许的吗?
答案 0 :(得分:5)
我想使用继承和多态,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) {}
};
答案 1 :(得分:3)
根据Jerry's answer中的讨论,如何通过不使用默认变量来简化问题:
class ThirdClass
{
StatLogger log;
public:
ThirdClass() : log(NullLogger()) {}
ThirdClass(const StatLogger& logger) : log(logger) {}
};
答案 2 :(得分:2)
使用派生实例作为基准引用的默认参数没有问题。
现在,你不能将非常量引用绑定到临时(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" );
}
只有当你想保留非const引用接口并同时提供默认实例时,你必须提供一个静态实例,但我建议不要这样做:
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() ) {}
答案 3 :(得分:1)
对于默认值,我相信您必须提供默认值...
ThirdClass(StatLogger *logger = NULL)
例如
答案 4 :(得分:0)
呃,我知道这是一个愚蠢的问题,但我遇到了同样的问题,在阅读了所有提出的答案和评论之后,我提出了一个稍微不同的解决方案。
我认为它也可能适用于此处提供的问题实例,所以在这里:
使NullStartLogger成为singleton类型的对象!
对我而言,它非常优雅和整洁。很快,单例是一个你无法随意构造的对象,因为只有一个实例可以随时存在。 (或者,在第一次使用之前可能有0个实例,因为您可以推迟初始化)。您当然只能将单例功能添加到派生类中,而父类的所有其他实例(和派生)都可以正常初始化和创建。但是,如果NullStatLogger
在我的情况下只是一个虚拟类,它不会在外部存储任何数据,并且根据实例/ init参数不具有不同的行为,单身人士很适合。
这是一段简短的代码剪辑,让您的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());
};
我知道这肯定对任何提出这个问题的人都没有帮助,但希望它可以帮助其他人。