2

我正在尝试使用interface类,并且具有以下class结构:

IBase.h:

#pragma once
class IBase
{
protected:
    virtual ~IBase() = default;

public:
    virtual void Delete() = 0;

    IBase& operator=(const IBase&) = delete;
};

IQuackable.h:

#ifndef IQUACKABLE
#define IQUACKABLE

#include "IBase.h"
#include <iostream>


class IQuackable : public IBase
{
protected:
    IQuackable() = default;
    ~IQuackable() = default;

public:
    virtual void Quack() = 0;

    static IQuackable* CreateInstance();
};

#endif // 

野鸭鸭.h:

#pragma once
#include "IQuackable.h"

class MallardDuck : public IQuackable
{
private:
    MallardDuck();

protected:
    ~MallardDuck();

public:
    void Delete() override;
    void Quack() override;

    friend IQuackable* IQuackable::CreateInstance();
};

野鸭鸭.cpp:

#include "MallardDuck.h"

MallardDuck::MallardDuck() {}

MallardDuck::~MallardDuck() {}

void MallardDuck::Delete() { delete this; }

void MallardDuck::Quack()
{
    std::cout << "Quack!\n";
}

IQuackable* IQuackable::CreateInstance()
{
    return static_cast<IQuackable*>(new MallardDuck());
}

我还创建了类 RedHeadDuck.h 和 .cpp,其声明和定义与 MallardDuck 相同。

最后,主类代码:

#include "MallardDuck.h"
#include "RedHeadDuck.h"

int main()
{
    IQuackable* mallardDuck = MallardDuck::CreateInstance();
    IQuackable* redHeadDuck = RedHeadDuck::CreateInstance();

    mallardDuck->Quack();
    redHeadDuck->Quack();
}

在这里我遇到了两个错误:


LNK2005“公共:静态类 IQuackable * __cdecl IQuackable::CreateInstance(void)”(?CreateInstance@IQuackable@@SAPAV1@XZ) 已在 MallardDuck.obj 中定义”。

LNK1169“找到一个或多个多重定义的符号”。


我发现,问题出在双重定义上,但它是如何解决的呢?

我已经阅读了有关 Header 保护的信息,但据我所知,在这种情况下它无济于事。人们也写了关于内联函数的文章,但我还没有意识到如何在这里使用它。

我能做些什么?

4

1 回答 1

0

目标

我想这些是你试图通过采用所有复杂模式来获得的:

  1. 接口,即“多种类型,同一组方法”
  2. 某种 抽象工厂模式,即您想要一个“实例化器”,它提供一个静态方法(与全局函数没有太大区别)来调用,并返回派生类的实例
  3. 禁止用户直接调用派生类的ctors
  4. Delete()通过实现该方法来照顾 dtors

要求 1-3

至少有 3 种方法可以满足要求 1-3,如下所述:

1.派生类隐藏其基类的静态方法

这是最简单的方法,它完全能够处理当前的main.cpp. 派生类可以覆盖其基类的静态方法。

在文件MallardDuck.hRedHeadDuck.h

    // Replace this:
    // friend IQuackable* IQuackable::CreateInstance();

    // With this:
    static IQuackable* CreateInstance();

在文件中MallardDuck.cpp(和RedHeadDuck.cpp类似的):

// Replace this:
// IQuackable* IQuackable::CreateInstance() {
//     return static_cast<IQuackable*>(new MallardDuck());
// }

// With this:
IQuackable* MallardDuck::CreateInstance() {
    return new MallardDuck();
}

这样做的问题是:其他不覆盖和隐藏的派生类CreateInstance()仍将IQuackable::CreateInstance()作为“后备”公开。因此:

  1. 如果您实际上没有实现IQuackable::CreateInstance()(到目前为止,您不必),那么一旦通过派生类调用它,代码将无法编译并且不会给出其他人可以理解的理由;或者
  2. 如果你选择实现它,你最好在其中抛出一个异常,这可能会让你的用户感到惊讶;否则您将不得不返回 anullptr或某物,这是 C++ 中最糟糕的做法(这就是我们在 C 中所做的,因为它没有语言级别的错误处理支持;任何不能完成其工作的 C++ 函数都不应返回)。

无论哪种方式都不优雅。

2.采用抽象工厂模式

这种模式需要一个协作的“工厂类”,它是抽象的;那么每当你派生出一个具体的 quackable 时,也要派生出它的工厂。

在你的情况下,你需要勾勒出 a IQuackableFactory,暴露IQuackableFactory::CreateInstance(),然后导出 aMallardDuckFactory和 a RedHeadDuckFactory

已经有很多很好的例子,所以我不会在这里演示。

3. 使用 CRTP 进行特征注入

还有另一种做事方式。CreateInstance()你实际上是在提供一个“给我这个类的一个实例!” 特征。通常我们使用 CRTP(奇怪的重复模板模式)将某个特征“注入”到一个类中。

首先写这个文件EnableCreateInstance.hpp

#ifndef _ENABLECREATEINSTANCE_HPP_
#define _ENABLECREATEINSTANCE_HPP_

template <class T>
struct EnableCreateInstance {
    static T* CreateInstance() { return new T(); }
};

#endif //_ENABLECREATEINSTANCE_HPP_

然后在MallardDuck.h

// Add:
#include "EnableCreateInstance.hpp"

class MallardDuck : public IQuackable, public EnableCreateInstance<MallardDuck> {
private:
    MallardDuck();

    friend class EnableCreateInstance<MallardDuck>;

    ...

在文件RedHeadDuck.h中做类似的事情:包括头,公开继承EnableCreateInstance<RedHeadDuck>,并声明EnableCreateInstance<RedHeadDuck>为朋友类。

这提供了更大的灵活性:您仍然提供接口CreateInstance(),但以一种不那么“激进”的方式:派生类可以自由选择是否提供CreateInstance()。如果他们这样做,只需继承并(如果 ctor 设为私有)声明友谊;如果不是,则省略额外的继承。

要求 4

好吧,实际上您可以delete this在非静态非 dtor方法中使用。但:

  1. 您必须确保(这可能很困难)不再访问已删除对象的数据成员或虚函数,否则会导致未定义的行为;
  2. 您为用户留下了指向已删除对象的悬空指针。

因此,我们很少在现代 C++ 中提供这样的“删除器”。您可以通过智能指针获得它可能提供的所有好处,以及避免 UB 的能力等等。

于 2022-01-27T09:46:07.067 回答