"One or more multiply defined symbols found" .静态和友元功能



我正在尝试使用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 // 

野鸭:

#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 "public: static class IQuackable

* __cdecl IQuackable::CreateInstance(void)" (?CreateInstance@IQuackable@@SAPAV1@XZ)已经在MallardDuck.obj中定义"。

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


正如我发现的那样,问题在于双重定义,但它如何解决?

我已经阅读了有关标题守卫的信息,但是,据我了解,在这种情况下它无济于事。人们也写了内联函数,但我还没有意识到它在这里如何使用。

我能做什么?

目标

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

  1. 接口,即"多种类型,同一组方法">
  2. 某种抽象的工厂模式,即,你想要一个"实例化器",它提供一个静态方法(与全局函数没有太大区别)来调用,并返回派生类的实例
  3. 禁止用户直接调用派生类的 CTOR
  4. 通过实施Delete()方法处理 DTOR

要求 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. 如果你选择实现它,你最好在其中抛出一个异常,这可能会让你的用户感到惊讶;否则你将不得不返回一个nullptr或其他东西,这是C++中最糟糕的做法(这就是我们在 C 中所做的,因为它没有语言级别的错误处理支持;任何无法完成其工作的C++函数都不应该返回)。

无论哪种方式都不优雅。

2. 采用抽象工厂模式

这种模式需要一个合作的"工厂类",这是抽象的;然后,每当你推导出一个具体的庸医时,也导出它的工厂。

在你的情况下,你需要勾勒出一个IQuackableFactory,暴露IQuackableFactory::CreateInstance(),然后推导出一个MallardDuckFactory和一个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

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

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

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

最新更新