前言:我在C ++方面相对缺乏经验,所以这很可能是第1天的n00b问题。
我正致力于一些长期目标是可跨多个操作系统移植的东西。我有以下文件:
Utilities.h
#include <string>
class Utilities
{
public:
Utilities() { };
virtual ~Utilities() { };
virtual std::string ParseString(std::string const& RawString) = 0;
};
UtilitiesWin.h (对于Windows类/实现)
#include <string>
#include "Utilities.h"
class UtilitiesWin : public Utilities
{
public:
UtilitiesWin() { };
virtual ~UtilitiesWin() { };
virtual std::string ParseString(std::string const& RawString);
};
UtilitiesWin.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'
所以在这种情况下,我想要做的是使用抽象类(Utilities),就像接口一样,让它知道去实现的版本(UtilitiesWin)。
显然我做错了什么,但我不确定是什么。在我写这篇文章的过程中,我发现在UtilitiesWin实现的Utilities抽象类之间可能存在一个重要的联系,我错过了,但我不知道在哪里。我的意思是,以下作品
#include <string>
#include "UtilitiesWin.h"
void SomeProgram::SomeMethod()
{
Utilities *u = new UtilitiesWin();
StringData = u->ParseString(StringData); // StringData defined elsewhere
}
但这意味着我必须稍后有条件地浏览不同的版本(例如UtilitiesMac()
,UtilitiesLinux()
等。)
我在这里错过了什么?
答案 0 :(得分:7)
Utilities *u = new Utilities();
告诉编译器创建Utilities
类的新实例; UtilitiesWin
扩展它的事实不一定是已知的,也不会影响它。可能有很多类扩展Utilities
,但您告诉编译器创建Utilities
的新实例,而不是那些子类。
听起来你想使用工厂模式,即在Utilities
中创建一个静态方法,返回指向特定实例的Utilities*
:
static Utilities* Utilities::make(void) {return new UtilitiesWin();}
在某些时候,你将不得不实例化一个非抽象的子类;那时无法指定UtilitiesWin
答案 1 :(得分:2)
你似乎对你想要的东西感到困惑;您必须在某个阶段告诉计算机它将使用哪种实用程序,但是您已经设定的形状只需要
#ifdef windows
Utilities* u = new UtilitiesWin();
#endif
#ifdef spaceos3
Utilities* u = new UtilitiesSpaceOS3();
#endif
一旦进入程序,大多数源文件只能调用你的方法,而不知道它是什么类型的 - 这就是我认为你的目标。
答案 2 :(得分:2)
在C ++中,你不能实例化抽象类,这正是你在这里要做的:
Utilities *u = new Utilities();
我很不清楚为什么你想要实例化这样一个类,如果你能这样做你会怎么做(你做不到)。您不能将实例化用作接口 - 类定义提供了。
答案 3 :(得分:2)
你正在“正确”,你必须实例化一个具体的类型。有共同的解决方案。
是的,你必须决定在哪个类实例化某个地方。
这个的实现取决于这个决定的标准:它是否为二进制文件固定?每个过程都有相同的选择吗?或者它是否会针对SomeProgram
的每个实例进行更改?
在您提到的具体课程之前,可能会在编译时做出决定,类似于Tom所建议的。
其次,SomeProgram
本身不应该做出此选择。而是应该从外部配置类型或实例。最简单的方法是将具体实例传递给SomeProgram的构造函数:
class SomeProgram
{
private:
Utilities * m_utilities;
public:
Someprogram(Utilities * util) : m_utilities(util) {}
}
请注意SomeProgram
只“知道”抽象类,没有具体的类。
对于延迟施工,请使用工厂。如果实用程序类应按上述方式注入,创建成本高昂,但大部分时间都不需要,您可以注入工厂:将UtilityFactory
传递给类,SomeProgram可以使用它来按需创建所需的实例。实际的工厂实现决定了要选择的具体类。有关详情,请参阅Factory pattern。
如果这是一个常见问题,请查看Inversion of Control(IoC) - 有几个库实现可以让您更轻松。它已经成为一个流行的单元测试后的流行词,用模拟取代“真正的”实现必须永久发生。 (我还在等待一个完整的MockOS)。但是,我没有在实践中对任何真正需要图书馆的应用程序进行过工作,而且很可能对你的问题有些过分。