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.h
andRedHeadDuck.h
:In file
MallardDuck.cpp
(andRedHeadDuck.cpp
similarly):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; ornullptr
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
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 aMallardDuckFactory
and 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.h
do 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 this
in 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.