代码之家  ›  专栏  ›  技术社区  ›  Tom Kidd

关于这个抽象类实现,我没有得到什么?

  •  1
  • Tom Kidd  · 技术社区  · 14 年前

    序言:我在C++方面相对缺乏经验,所以这很可能是一个1天的N0B问题。

    我正在研究一种长期目标是跨多个操作系统可移植的东西。我有以下文件:

    公用事业公司

    #include <string>
    
    class Utilities
    {
    public:
        Utilities() { };
        virtual ~Utilities() { };
    
        virtual std::string ParseString(std::string const& RawString) = 0;
    };
    

    用英语怎么说? (对于Windows类/实现)

    #include <string>
    #include "Utilities.h"
    
    class UtilitiesWin : public Utilities
    {
    public:
        UtilitiesWin() { };
        virtual ~UtilitiesWin() { };
    
        virtual std::string ParseString(std::string const& RawString);
    };
    

    实用程序win.cpp

    #include <string>
    #include "UtilitiesWin.h"
    
    std::string UtilitiesWin::ParseString(std::string const& RawString)
    {
        // Magic happens here!
        // I'll put in a line of code to make it seem valid
        return "";
    }
    

    所以在我代码的其他地方我有这个

    #include <string>
    #include "Utilities.h"
    
    void SomeProgram::SomeMethod()
    {
        Utilities *u = new Utilities();
        StringData = u->ParseString(StringData); // StringData defined elsewhere
    }
    

    编译器(Visual Studio 2008)即将死于实例声明

    c:\somepath\somecode.cpp(3) : error C2259: 'Utilities' : cannot instantiate abstract class
            due to following members:
            'std::string Utilities::ParseString(const std::string &)' : is abstract
            c:\somepath\utilities.h(9) : see declaration of 'Utilities::ParseString'
    

    因此,在本例中,我想做的是像使用接口一样使用抽象类(实用程序),并让它知道要转到实现的版本(实用程序Win)。

    很明显我做错了什么,但我不确定。在我写这篇文章的时候,我突然想到,在实用程序抽象类的utilitieswin实现之间可能存在一个我错过的关键连接,但是我不确定在哪里。我是说,下面的作品

    #include <string>
    #include "UtilitiesWin.h"
    
    void SomeProgram::SomeMethod()
    {
        Utilities *u = new UtilitiesWin();
        StringData = u->ParseString(StringData); // StringData defined elsewhere
    }
    

    但这意味着我以后必须有条件地浏览不同的版本(即, UtilitiesMac() , UtilitiesLinux() 等)

    我错过了什么?

    4 回复  |  直到 14 年前
        1
  •  7
  •   Michael Mrozek    14 年前
    Utilities *u = new Utilities();
    

    告诉编译器生成 Utilities 阶级;事实上 UtilitiesWin 扩展它不一定是已知的,也不影响它。可能有很多类在扩展 公用事业 但是您告诉编译器创建 公用事业 不是那些子类。

    听起来您想使用工厂模式,即在 公用事业 返回一个 Utilities* 它指向一个特定的实例:

    static Utilities* Utilities::make(void) {return new UtilitiesWin();}
    

    在某种程度上,您必须实例化一个非抽象的子类;没有办法指定 效用赢 在那一点

        2
  •  2
  •   Tom Womack    14 年前

    你似乎对你想要什么有点困惑;你必须在某个阶段告诉计算机它要使用的实用程序的实现,但是根据你设定的形状,你只需要

    #ifdef windows
     Utilities* u = new UtilitiesWin();
    #endif
    #ifdef spaceos3
     Utilities* u = new UtilitiesSpaceOS3();
    #endif
    

    一旦进入程序,大多数源文件就可以调用u的方法,而不知道它是什么类型的u——我认为这就是你的目标。

        3
  •  2
  •   anon    14 年前

    在C++中,你不能实例化抽象类,这正是你在这里试图做的:

    Utilities *u = new Utilities();
    

    我很不清楚你为什么要实例化这样一个类,如果你能这样做(你不能这样做),你会用它做什么。不能将实例化用作接口-类定义提供了这一点。

        4
  •  2
  •   peterchen    14 年前

    你要把它“弄好”,你必须实例化一个具体的类型。有一些共同的解决办法。

    是的,您必须决定在某个地方实例化哪个类。
    这一决定的实现取决于该决定的标准:它是否固定用于二进制文件?每个过程都有相同的选择?或者它是否会因 SomeProgram ?

    在您提到具体的类之前,可以在编译时做出决定,类似于Tom的建议。

    第二, 一些程序 不应该自己做这个选择。 相反,类型或实例应该可以从外部进行配置。最简单的方法是将具体实例传递给someprogram的构造函数:

    class SomeProgram
    {
       private:
         Utilities * m_utilities;
    
       public:
         Someprogram(Utilities * util) : m_utilities(util) {}
    
    }
    

    注意 一些程序 只有“知道”抽象类,没有具体类。

    对于延迟施工,使用工厂。 如果像上面那样注入实用程序类,创建起来很昂贵,但大多数时候不需要,那么您应该注入一个工厂:通过 UtilityFactory 类,某些程序可以使用该类按需创建所需的实例。实际工厂实现决定要选择的具体类。见 Factory pattern 更多。

    如果这是一个常见的问题 Inversion of Control (IOC)-有几个库实现可以简化这一过程。在激烈的单元测试之后,它已经成为一个时髦的词,在那里用mock替换“真正的”实现必须永久发生。(不过,我仍在等待一个完整的模拟模型)。但是,我还没有开发过任何一个在实践中非常需要suhc库的应用程序,这很可能会对您的问题造成严重的破坏。