I'm trying to use interface classes and I have the following class structure:
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 //
MallardDuck.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:
#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.
And, finaly, main class code:
#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".
LNK1169 "one or more multiply defined symbols found".
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. 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:
Delete()methodRequirement 1-3
There are at least 3 ways to meet requirement 1-3, as explained below:
1. Derived classes hiding static method of their base
This is the easiest way, and it's fully capable of current
main.cpp. Derived classes can override static methods of their base class.In file
MallardDuck.handRedHeadDuck.h:In file
MallardDuck.cpp(andRedHeadDuck.cppsimilarly):The problem with this is that: other derived classes that don't override and hide
CreateInstance()will still exposeIQuackable::CreateInstance()as a "fallback". Thus: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; ornullptror 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
This pattern requires a cooperating "factory class", which is abstract; then whenever you derive a concrete quackable, derive also its factory.
In your case you'll need to sketch out a
IQuackableFactory, exposingIQuackableFactory::CreateInstance(), then derive aMallardDuckFactoryand aRedHeadDuckFactory.There are plenty of good examples already, so I won't demonstrate here.
3. Feature injection by using CRTP
There's yet another way of doing things. By
CreateInstance()you're actually providing a "Give me an instance of this class!" feature. Typically we use the CRTP (curiously recurring template pattern) to "inject" a certain feature into a class.First write this file
EnableCreateInstance.hpp:Then in
MallardDuck.h:In file
RedHeadDuck.hdo the similar: include header, publicly inheritEnableCreateInstance<RedHeadDuck>, and declareEnableCreateInstance<RedHeadDuck>as friend 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 provideCreateInstance(). If they do, just inherit and (if ctor made private) declare friendship; if not, omit the additional inheritance.Requirement 4
Well, actually you can use
delete thisin non-static non-dtor method. But:So, we seldom provide such "deleters" in modern C++. You can get all the benefits it may provide through smart pointers, plus the ability to avoid UBs and so much more.