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

单例继承,派生类不能在父类中实例化?

  •  2
  • yesraaj  · 技术社区  · 14 年前

    下面的代码基于环境变量实例化派生的singleton对象。编译器错误地说 error C2512: 'Dotted' : no appropriate default constructor . 我不明白编译器在抱怨什么。

    编辑: 修复了实现get instance方法时需要同时定义父类和派生类的问题。通过在单独的头文件中分离类定义,并在实现实例函数的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(){};
    
    };
    
    4 回复  |  直到 14 年前
        1
  •  2
  •   Michael Aaron Safyan    14 年前

    代码有几个问题:

    • 你的意思是回型 Singleton& const Singleton& . 您当前正在按值返回,该值试图调用复制构造函数,但不存在此类构造函数。
    • 虚线中的默认构造函数可能在singleton中不可用。我建议您让singleton成为dotted的朋友,以便它能够访问该构造函数。虽然不是百分之百确定。
    • 您忘记将函数print()设为虚拟函数,因此覆盖不会自行显示。
    • 你把“朋友”放错地方了;你需要声明辛格尔顿是虚线中的朋友,而不是在辛格尔顿之内。
    • 您不应该将singleton::instant的定义设为inline,因为它需要构造dotted的一个实例,为此,它需要查看dotted的定义。因此,您应该将其移动到一个源文件中,在该文件中,它可以分别看到dotten和singleton的完整定义。
    • 你需要把 Singleton* Singleton::instance_ = 0; 在某个源文件中。
    • 您的 if(!style) 节;当前,如果已设置样式环境变量,但未设置为“点”,则最终将返回空的singleton。

    除此之外,我强烈建议你 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_样式而不是仅使用样式;换句话说,如果要使用环境变量路由,至少要限定其名称。

        2
  •  1
  •   Hans Passant    14 年前

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

    class Singleton {
    public:
        static Singleton & instant();
        // etc..
    };
    
    class Dotted : public Singleton {
        // etc..
    };
    
    // Now it works:
    Singleton & Singleton::instant() {
        // etc..
    }
    
        3
  •  0
  •   anon    14 年前

    我不明白这是怎么回事。此时:

    instance_ = new Dotted();
    

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

    error: invalid use of incomplete type 'struct Dotted'
    

    你在用哪个编译器?

        4
  •  0
  •   UncleBens    14 年前

    我得到的第一个错误是: strcmp not declared . 提示:在 <cstring> ( <string.h> )头球。

    之后,下一个错误是:

    instance_ = new Dotted();
    

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

    如果使用forward声明,则必须将声明和实现分开,这样就可以在定义完类型之后执行需要完整类型的操作。