我目前正在从事C ++项目,我正在处理自制类 CSound 并使用 CSoundEngine 处理其实例,允许自己创建一个CSound(存储在我的CSoundEngine中)并返回指向它的指针。
事实上,出于设计目的,我希望返回一个指向接口的指针。例如,可以将真实对象存储在向量中,并通过C ++(或Java)中的接口提供它吗?
如果答案是肯定的,是否可以存储一个可以扩展的通用对象的向量,并根据子类返回一个特定的接口?
谢谢!
答案 0 :(得分:0)
所以,如果我理解正确,你会有类似的东西:
class CSound {
// Whatever
};
class CSoundEngine {
// A lot of stuff
CSound* createSound(/* ... */);
};
你不希望CSound返回,但是一个界面,虽然它内部存储在一个具有特定类型的矢量中?
第一个问题 - 界面 - 很容易解决:
创建接口并从中派生。
class ISound {
public:
ISound(const ISound&) = delete;
ISound(ISound&&) = delete;
ISound& operator =(const ISound&) = delete;
ISound& operator =(ISound&&) = delete;
virtual ~ISound() = default;
// Your ISound public API here as pure virtual methods, e.g.:
virtual const std::string name() const = 0;
protected:
ISound() = default;
};
class ILoopable {
public:
ILoopable(const ILoopable&) = delete;
ILoopable(ILoopable&&) = delete;
ILoopable& operator =(const ILoopable&) = delete;
ILoopable& operator =(ILoopable&&) = delete;
virtual ~ILoopable() = default;
// Your ILoopable public API here as pure virtual methods, e.g.:
virtual const bool isLoopingActive() const = 0;
virtual bool setLoopingActive(bool) = 0;
protected:
ILoopable() = default;
};
class CDefaultSound
: public ISound {
public:
CDefaultSound () = default;
// ISound implementation
inline const std::string name() const { return mName; }
private:
std::string mName;
};
class CLoopableSound
: public ISound,
public ILoopable {
public:
CLoopableSound()
: ISound(),
ILoopable(),
mLoopingActive(true),
mName("")
{
}
// ISound implementation
inline const std::string name() const { return (mName + "(Loopable)"); }
// ILoopable implementation
inline const bool isLoopingActive() const { return mLoopingActive; }
inline bool setLoopingActive(bool active) { mLoopingActive = active; }
private:
bool mLoopingActive;
std::string mName;
};
int main()
{
// Now you can do something like this, for example, using polymorphism
// in your CSoundEngine (see below)...
ISound *pDef = new CDefaultSound();
ISound *pLoopable = new CLoopableSound();
}
如果你只想使用源自ISound的CSound,那么你不需要多个课程,但是我不明白使用界面的意义。 重要提示:由于纯虚拟接口方法,您无法实现接口类,因此您必须使用指针或RAII指针,如shared_ptr或unique_ptr(我还是推荐RAII ......)
第二个问题 - 在向量中存储特定类型 - 要困难得多,因为您需要单个向量foreach允许类型。要么!您存储接口实例并仅使用interface-methods!
class DefaultSoundCreator {
static ISound* createSound(/* Criteria */) { ... }
};
template <typename TSoundCreator>
class CSoundEngine {
public:
CSoundEngine()
: mSoundCreator() {
}
std::shared_ptr<ISound> createSound(/* some criteria */);
private:
std::vector<std::shared_ptr<ISound>> mSounds;
};
// cpp
std::shared_ptr<ISound> CSoundEngine::createSound(/* some criteria */) {
// Use criteria to create specific sound classes and store them in the mSOunds vector.
ISound *pSound = TSoundCreator::createSound(/* forward criteria for creation */);
std::shared_ptr<ISound> pSoundPtr = std::shared_ptr<ISound>(pSound);
mSounds.push_back(pSoundPtr);
return pSoundPtr;
}
int main() {
std::unique_ptr<CSoundEngine<DefaultSoundCreator>> pEngine = std::make_shared<CSoundEngine<DefaultSoundCreator>>();
std::shared_ptr<ISound> pSound = pEngine->createSound(/* Criteria */);
}
这样你可以依赖ISound提供的功能,但是通过指定Creator类,你可以使用通用声音引擎控制声音创建。
现在使用接口基类的类型擦除的实际问题:你知道你在索引2处存储了一个CLoopableSound,但由于声音引擎的方法createSound(),你只能使用ISound-Interface方法:std :: shared_ptr的;
如何访问ILoopable-behavior?
这就是重点,它变得具有哲学性......我建议阅读:
https://akrzemi1.wordpress.com/2013/11/18/type-erasure-part-i/ Type erasure techniques https://aherrmann.github.io/programming/2014/10/19/type-erasure-with-merged-concepts/
我喜欢使用的一种技术:
class CLoopableSound
: public ISound {
// All the above declarations and definitions
// AND:
static std::shared_ptr<CLoopableSound> fromISound(const std::shared_ptr<ISound>& other, bool *pSuccess = nullptr) {
std::shared_ptr<CLoopableSound> p = std::static_pointer_cast<CLoopableSound>(other);
if(pSuccess)
*pSuccess = p.operator bool();
return p;
}
};
// Use like
std::shared_ptr<CLoopableSound> pLoopable = CLoopableSound::fromISound(pSoundEngine->getSound(...));
if(pLoopable) {
// Use
}
最后,您当然可以将fromISound-function作为模板并使用强制转换来仅访问ILoopable而不是CLoopableSound等。