【问题标题】:Singleton with inheritance, Derived class is not able to get instantiated in parent?具有继承的单例,派生类无法在父类中实例化?
【发布时间】:2010-05-09 10:52:32
【问题描述】:

下面的代码根据环境变量实例化一个派生的单例对象。编译器错误提示 error C2512: 'Dotted' : no appropriate default constructor。我不明白编译器在抱怨什么。

编辑: 修复了实现需要定义父类和派生类的 get 实例方法的问题。通过将类定义分隔在单独的头文件中,并将其包含在实现实例函数的 Singleton.cpp 中。

Mainfile – 1
#include <iostream>
#include <string>
#include "Singleton.h"
using namespace std;

int main(){

     Singleton::instant().print();
     cin.get();
}
Singleton.h
#pragma once
#include <iostream>
using std::cout;
class Singleton{
public:
    static Singleton & instant();
    virtual void print(){cout<<"Singleton";}
protected:
    Singleton(){};
private:
    static Singleton * instance_;
    Singleton(const Singleton & );
    void operator=(const Singleton & );

};

Singleton.cpp
#include "Singleton.h"
#include "Dotted.h"
Singleton * Singleton::instance_ = 0;
Singleton & Singleton::instant(){
    if (!instance_)
    {
        char * style = getenv("STYLE");
        if (style){
            if (strcmp(style,"dotted")==0)
            {
                instance_ = new Dotted();
                return *instance_; 
            }           else{
                instance_ = new Singleton();
                return *instance_;
            }       
        }

        else{
            instance_ = new Singleton();
            return *instance_;
        }
    }
    return *instance_;

}
Dotted.h

#pragma once
class Dotted;

class Dotted:public Singleton{
public:
    friend class Singleton;
    void print(){cout<<"Dotted";}
    private:
        Dotted(){};

};

【问题讨论】:

  • 我不是 C++ 程序员,但是像这样的超类可以使用受保护的构造函数吗?难道你不必让单身人士成为dotted的朋友吗?
  • @Lasse V. Karlsen 都没有工作:公开构造函数并使单例的 Dotted 成为朋友。

标签: c++ inheritance singleton compiler-errors


【解决方案1】:

你的代码有几个问题:

  • 您的意思是返回类型Singleton&amp;const Singleton&amp;。您当前正在按值返回,它正在尝试调用复制构造函数,并且不存在这样的构造函数。
  • 您在 Dotted 中的默认构造函数可能在 Singleton 中不可用。我建议您让 Singleton 成为 Dotted 的朋友,以便它能够访问该构造函数。虽然不是 100% 确定这一点。
  • 您忘记将 print() 函数设为虚拟函数,因此您的覆盖不会显示出来。
  • 你把“朋友”放错地方了;您需要在 Dotted 中声明 Singleton 为 Dotted 的朋友,而不是在 Singleton 中。
  • 您不应将 Singleton::instant 的定义内联,因为它需要构造 Dotted 的实例,并且为了做到这一点,它需要查看 Dotted 的定义。所以你应该把它移到一个源文件中,它可以分别看到 Dotted 和 Singleton 的完整定义。
  • 您需要将Singleton* Singleton::instance_ = 0; 放入源文件中的某个位置。
  • if(!style) 部分中缺少 else 子句;目前,如果设置了 STYLE 环境变量,但未设置为“dotted”,那么您最终会返回一个空单例。

除上述之外,我强烈建议您avoid environment variables and singletons。它们都是“共享可变状态”的例子,可能会导致很多混乱。单例虽然在“设计模式”书籍中出现了很长一段时间,但现在被理解为设计反模式。这是一种更灵活的方法,让您可以传递一个接口,并且只是碰巧实例化一次,而不是在它的 API 中只存在一次这一事实。

例如,对于您的特定情况,我建议如下:

class Printer
{
    public:
        virtual ~Printer(){}
        virtual void print()const = 0
};

class StringPrinter : public Printer
{
    public:
         StringPrinter() : _str("") {}
         StringPrinter(const std::string& str) : _str(str) {}
         StringPrinter(const StringPrinter& o) : _str(o._str) {}
         virtual ~StringPrinter(){}
         virtual void print()const{ std::cout << _str << std::endl; }
         StringPrinter& operator=(const StringPrinter& o){ _str = o._str; return *this;}
    private:
         std::string _str;         
};

然后,在您之前使用 Singleton 的任何类中,只需获取一个 const Printer& 对象。并打印到该对象。在其他地方,您可以有条件地构造 StringPrinter("Singleton") 或 StringPrinter("dotted")。或者可能是该接口的其他一些实例,尽管我建议使用QSettings 或某种配置文件来代替环境变量,或者至少使用 MYAPPLICATIONNAME_STYLE 而不仅仅是 STYLE;换句话说,如果你要走环境变量路线,至少要限定它的名字。

【讨论】:

  • 我应该指出,“避免单身”是一个见仁见智的问题。我在整个编程生涯中都使用过它们,没有任何问题。
  • @Neil,我同意这是一种观点,但是单例和全局变量经常会导致绝对扭曲的代码。恕我直言,一种更好的方法是传入一个接口并简单地拥有它,以便您碰巧只构造一个实现它的对象。
  • @Michael Aaron Safyan 我不能接受你的意见,因为只要你知道如何不使用单例,单例就非常有用。还有它的阴暗角落。在现代 C++ 和更有效的 C++ 中有一章专门讨论单例。
  • @Michael Aaron Safyan 哎呀谢谢。代码仍然编译错误。
  • 现代 C++ 和更有效的 C++ 中有专门的一章,因为单例经常被使用并且很难正确使用:这并不意味着它们应该被使用。出于方便,我也使用它们,但是全局变量会损害可测试性并针对依赖注入运行。在一个理想的世界里,我们也许只能使用依赖注入,但它可能会扭曲设计,在这一点上它似乎毫无用处,而且这也意味着暴露实现细节:x 不管怎样,我个人已经开始用 Monoid 替换我的 Singletons,它有隐藏共享状态的优势。
【解决方案2】:

这不是一个很好的错误消息。问题是编译器无法为构造函数调用生成代码,它还没有看到 Dotted 类的定义。 C++ 编译器仍然是单遍编译器。你不能内联写方法,你必须移动它。

class Singleton {
public:
    static Singleton & instant();
    // etc..
};

class Dotted : public Singleton {
    // etc..
};

// Now it works:
Singleton & Singleton::instant() {
    // etc..
}

【讨论】:

    【解决方案3】:

    我不明白这是如何工作的。此时:

    instance_ = new Dotted();
    

    Dotted 是不完整的类型。使用 g++ 4.4.1 我得到:

    error: invalid use of incomplete type 'struct Dotted'
    

    您使用的是哪个编译器?

    【讨论】:

    • @Neil Butterworth Microsoft Visual C++ 2005
    • @yesraj 我认为您发布的代码与您正在编译的代码不同。
    • @Neil Butterworth 不,我正在粘贴相同的代码。我认为@Michael Aaron Safyan 的回答应该有效。问题在于没有在单独的翻译单元中分离类。
    【解决方案4】:

    我得到的第一个错误是:strcmp not declared。提示:它在 &lt;cstring&gt; (≈ &lt;string.h&gt;) 标头中。

    之后下一个错误是:

    instance_ = new Dotted();
    

    “不完整类型的无效使用。”

    如果你使用前向声明,你必须将声明和实现分开,这样你就可以在定义完类型后做需要完整类型的事情。

    【讨论】:

      猜你喜欢
      • 2021-08-24
      • 1970-01-01
      • 2020-02-18
      • 1970-01-01
      • 2020-06-14
      • 2018-03-14
      • 1970-01-01
      • 2012-07-04
      • 1970-01-01
      相关资源
      最近更新 更多