Il me semble que ce que vous voulez mettre en œuvre est un Policy-Based Design. Je ne suis pas sûr ce que Model
et Strategy
font exactement, mais il semble que Model
est la classe racine, et Strategy
est la classe Policy, que les utilisateurs voudraient fournir dans certains cas pour effectuer une manipulation spéciale. Il semble également que la seule raison pour laquelle vous gardez un pointeur sur l'objet Strategy<T>
est que vous pouvez y appeler des fonctions.
Dans ce cas, vous pouvez concevoir votre classe comme ceci:
template<class Strategy>
class Model : public Strategy {
public:
void DoSomething()
{
// magic happens (we will fill this in shortly)
};
};
Vous appeler des méthodes sur la classe Strategy
pour faire votre magie. En laissant les utilisateurs définir leur propre classe Strategy
, vous leur donnez l'opportunité de définir leur propre "magie". Vous devez appliquer des règles sur la méthode que la classe Strategy
fournira au minimum, afin que vous puissiez appeler ces méthodes dans Model
. Par exemple, supposons que Model
est en fait une sorte de gestionnaire de ressources, capable d'être un simple pointeur intelligent, ou quelque chose d'autre comme un gestionnaire de ressources pour une section critique de Windows. Renommons Model
à auto_resource
et Strategy
deviendra release_policy
et sera responsable de la libération de toute ressource qui lui a été affectée. Dans ce cas, vous pourriez avoir:
class pointer_release_policy
{
public:
template<class Object> void release(Object* obj) { delete obj; }
};
template<class Managed, class release_policy>
class auto_resource : public release_policy
{
public:
// ... ctors etc defined here
~auto_resource()
{
release_policy::release(managed_);
}
private:
Managed managed_;
};
que vous pourriez utiliser pour std::string
pointeurs comme ceci:
typedef auto_resource<std::string*, pointer_release_policy> string_ptr;
string_ptr my_str;
... et quand my_str
tombe de la pile, la méthode release
sera automatiquement appelé .
Plus tard, vous voulez ajouter une nouvelle politique pour la libération de Windows mutex HANDLE
s:
class handle_release_policy
{
public:
template<class Handle> void release(Handle h)
{
CloseHandle(h); // this is a WINAPI function that deallocates the specified resource
};
};
Vous pouvez utiliser ainsi:
typedef auto_resource<HANDLE, handle_resource_policy> handle_resource;
//... allocate & use the mutex...
handle_resource mutex = CreateMutex(0, 0, 0);
Bien sûr, afin de chair tout ça vous besoin d'ajouter des fonctionnalités pour assigner, copier, libérer etc. les ressources. Voici un exemple de travail complet qui met tout ensemble. J'ai fourni 2 séries de politiques, l'une pour Windows CRITICAL_SECTION
s, et un autre pour SOCKET
s:
class SimpleCopyPolicy
{
public:
template<class Resource> Resource copy(const Resource& rhs) const { Resource ret = rhs; return ret; }
protected:
~SimpleCopyPolicy(){};
};
class CritsecReleasePolicy
{
public:
template<class Handle> bool release(Handle& h)
{
DeleteCriticalSection(&h);
return true;
}
protected:
~CritsecReleasePolicy() {};
};
class CritsecLockPolicy // CRITICAL_SECTION lock/unlock policies
{
public:
template<class Handle> bool lock(Handle& h)
{
EnterCriticalSection(const_cast<CRITICAL_SECTION*>(&h));
return true;
}
template<class Handle> bool unlock(Handle& h)
{
LeaveCriticalSection(&h);
return true;
}
};
class SocketReleasePolicy
{
public:
template<class Handle> bool release(Handle h) { return 0 != closesocket(h); }
protected:
~SocketReleasePolicy(){};
};
template<class Resource, typename ReleasePolicy, typename CopyPolicy = SimpleCopyPolicy>
class simple_auto_resource : public ReleasePolicy, public CopyPolicy
{
public:
typedef simple_auto_resource<Resource,ReleasePolicy,CopyPolicy> base_type;
simple_auto_resource() : res(0) {}
simple_auto_resource(const Resource & r) : res(copy(r)) {}
~simple_auto_resource() { if(res) release(res); }
void clear() { if(res) release(res); res = 0; }
Resource& get() { return res; }
const Resource& get() const { return res; }
Resource detach() { Resource ret = res; res = 0; return ret; }
operator const Resource&() const { return get(); }
operator Resource&() { return get(); }
base_type& operator=(const Resource& rhs) { clear(); res = copy(rhs); return * this; }
template<class Comp> bool operator==(const Comp& rhs) const { return res == (Resource)rhs; }
template<class Comp> bool operator!=(const Comp& rhs) const { return res != (Resource)rhs; }
template<class Comp> bool operator<(const Comp& rhs) const { return res < (Resource)rhs; }
private:
Resource res;
};
typedef simple_auto_resource<CRITICAL_SECTION, CritsecReleasePolicy> auto_critsec;
typedef simple_auto_resource<SOCKET,SocketReleasePolicy> auto_socket;
Pour en savoir plus sur la conception basée sur des règles, voir Modern C++ Design.
...
Vous paramétrez un membre de données * dans 'DoSomething'. Comment compile-t-il même? Et d'où vient «T» dans 'Strategy < T >'? –