简体   繁体   English

“找到一个或多个多重定义的符号”。 Static 和朋友 function

[英]"One or more multiply defined symbols found". Static and friend function

I'm trying to use interface classes and I have the following class structure:我正在尝试使用interface类,并且我有以下class结构:

IBase.h: IBase.h:

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

public:
    virtual void Delete() = 0;

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

IQuackable.h: 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 // 

MallardDuck.h:野鸭鸭.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();
};

MallardDuck.cpp:野鸭鸭.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());
}

Also I've created class RedHeadDuck.h and.cpp with the same declaration and definition as MallardDuck.我还创建了 class RedHeadDuck.h 和.cpp,其声明和定义与 MallardDuck 相同。

And, finaly, main class code:最后,主要的 class 代码:

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

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

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

And here I got two errors:在这里我遇到了两个错误:


LNK2005 "public: static class IQuackable * __cdecl IQuackable::CreateInstance(void)" (?CreateInstance@IQuackable@@SAPAV1@XZ) already defined in MallardDuck.obj". LNK2005“公共:static class IQuackable * __cdecl IQuackable::CreateInstance(void)”(?CreateInstance@IQuackable@@SAPAV1@XZ) 已在 MallardDuck.obj 中定义。

LNK1169 "one or more multiply defined symbols found". LNK1169“找到一个或多个多重定义的符号”。


As I find out, the problem is in double definition, but how it fix?我发现,问题出在双重定义上,但它是如何解决的呢?

I've read about Header guards, but, as I understood, it can't help in this case.我读过有关 Header 防护装置的信息,但据我了解,在这种情况下它无济于事。 Also people write about inline functions, but I've not realized how it may be used here.人们也写了关于内联函数的文章,但我还没有意识到如何在这里使用它。

What can I do?我能做些什么?

Goals目标

I suppose these are what you are trying to obtain by adopting all the complicated patterns:我想这些是你试图通过采用所有复杂模式来获得的:

  1. interface, ie, "multiple types, same set of methods"接口,即“多种类型,同一组方法”
  2. some sort of abstract factory pattern , ie, you want a "instantiator" who provides a static method (which is not very different from a global function) to call, and returns instances of derived classes某种抽象工厂模式,即您想要一个提供 static 方法(与全局函数没有太大区别)的“实例化器”来调用,并返回派生类的实例
  3. prohibit users from directly calling the ctors of derived classes禁止用户直接调用派生类的ctors
  4. take care of the dtors by implementing the Delete() method通过实现Delete()方法来处理 dtors

Requirement 1-3要求 1-3

There are at least 3 ways to meet requirement 1-3, as explained below:至少有 3 种方法可以满足要求 1-3,如下所述:

1. Derived classes hiding static method of their base 1. 隐藏其基类的 static 方法的派生类

This is the easiest way, and it's fully capable of current main.cpp .这是最简单的方法,它完全能够处理当前的main.cpp Derived classes can override static methods of their base class.派生类可以覆盖其基础 class 的 static 方法。

In file MallardDuck.h and RedHeadDuck.h :在文件MallardDuck.hRedHeadDuck.h

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

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

In file MallardDuck.cpp (and RedHeadDuck.cpp similarly):在文件MallardDuck.cpp (和RedHeadDuck.cpp类似)中:

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

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

The problem with this is that: other derived classes that don't override and hide CreateInstance() will still expose IQuackable::CreateInstance() as a "fallback".这样做的问题是:其他不覆盖和隐藏CreateInstance()的派生类仍会将IQuackable::CreateInstance()公开为“后备”。 Thus:因此:

  1. if you don't actually implement IQuackable::CreateInstance() (so far, you don't have to), then once it is called via a derived class, the code won't compile and won't give a reason that's comprehensible to others;如果您实际上没有实现IQuackable::CreateInstance() (到目前为止,您不必),那么一旦通过派生的 class 调用它,代码将无法编译并且不会给出可理解的原因对他人; or或者
  2. if you choose to implement it, you'd better throw an exception within it, which may surprise your user;如果你选择实现它,你最好在其中抛出一个异常,这可能会让你的用户感到惊讶; otherwise you would have to return a nullptr or something, which is the worst practice in C++ (that's what we do in C since it has no language-level support for error handling; any C++ function that cannot fulfill its job should never return). otherwise you would have to return a nullptr or something, which is the worst practice in C++ (that's what we do in C since it has no language-level support for error handling; any C++ function that cannot fulfill its job should never return).

Either way is not elegant.无论哪种方式都不优雅。

2. Adopt abstract factory pattern 2.采用抽象工厂模式

This pattern requires a cooperating "factory class", which is abstract;这种模式需要一个协作的“工厂类”,它是抽象的; then whenever you derive a concrete quackable, derive also its factory.那么每当你派生出一个具体的 quackable 时,也要派生出它的工厂。

In your case you'll need to sketch out a IQuackableFactory , exposing IQuackableFactory::CreateInstance() , then derive a MallardDuckFactory and a RedHeadDuckFactory .在您的情况下,您需要绘制一个IQuackableFactory ,公开IQuackableFactory::CreateInstance() ,然后派生一个MallardDuckFactory和一个RedHeadDuckFactory

There are plenty of good examples already, so I won't demonstrate here.已经有很多很好的例子,所以我不会在这里演示。

3. Feature injection by using CRTP 3. 使用 CRTP 进行特征注入

There's yet another way of doing things.还有另一种做事方式。 By CreateInstance() you're actually providing a "Give me an instance of this class."通过CreateInstance() ,您实际上是在提供“给我这个 class 的实例”。 feature.特征。 Typically we use the CRTP ( curiously recurring template pattern ) to "inject" a certain feature into a class.通常我们使用 CRTP( 奇怪的重复模板模式)将某个特征“注入”到 class 中。

First write this file EnableCreateInstance.hpp :首先编写这个文件EnableCreateInstance.hpp

#ifndef _ENABLECREATEINSTANCE_HPP_
#define _ENABLECREATEINSTANCE_HPP_

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

#endif //_ENABLECREATEINSTANCE_HPP_

Then in MallardDuck.h :然后在MallardDuck.h中:

// Add:
#include "EnableCreateInstance.hpp"

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

    friend class EnableCreateInstance<MallardDuck>;

    ...

In file RedHeadDuck.h do the similar: include header, publicly inherit EnableCreateInstance<RedHeadDuck> , and declare EnableCreateInstance<RedHeadDuck> as friend class.在文件RedHeadDuck.h中执行类似操作:包含 header,公开继承EnableCreateInstance<RedHeadDuck> ,并将EnableCreateInstance<RedHeadDuck>声明为朋友 class。

This provides more flexibility: you're still providing an interface CreateInstance() , but in a less "aggressive" way: derived classes have their freedom to choose whether or not to provide CreateInstance() .这提供了更大的灵活性:您仍然提供接口CreateInstance() ,但以一种不那么“激进”的方式:派生类可以自由选择是否提供CreateInstance() If they do, just inherit and (if ctor made private) declare friendship;如果他们这样做,只需继承并(如果 ctor 设为私有)声明友谊; if not, omit the additional inheritance.如果没有,请省略额外的 inheritance。

Requirement 4要求 4

Well, actually you can use delete this in non-static non-dtor method.好吧,实际上您可以在非静态非 dtor方法中使用delete this But:但:

  1. You must ensure (which can be difficult) that no more access to the deleted object's data members or virtual functions are made, otherwise it causes undefined behaviors;您必须确保(这可能很困难)不再访问已删除对象的数据成员或虚函数,否则会导致未定义的行为;
  2. You leave your users with dangling pointers to the deleted object.您为用户留下了指向已删除 object 的悬空指针。

So, we seldom provide such "deleters" in modern C++.因此,我们很少在现代 C++ 中提供这样的“删除器”。 You can get all the benefits it may provide through smart pointers , plus the ability to avoid UBs and so much more.您可以通过智能指针获得它可能提供的所有好处,以及避免 UB 的能力等等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM