What's the best strategy for typedef'ing shared pointers?

[亡魂溺海] 提交于 2019-12-02 20:32:26

I don't like a library dictating the use of a particular smart pointer, but I tolerate it if it is necessary.

If you wish to force users to always use a shared_ptr to manipulate a widget, it's impossible, so don't even bother trying.

On the other hand, if you have a method from Widget which returns a boost::shared_ptr<Widget>, then providing a (sane) typedef might simplify the client code.

I would therefore promote the use of an inner typedef:

class Widget
{
public:
  typedef boost::shared_ptr<Widget> Ptr;

  Ptr AccessFirstChild();
}; // class Widget

in which case it's perfectly okay to #include the necessary headers.

Furthermore, it doesn't seem make sense to typedef boost::shared_ptr during the declaration of Widget itself—we seem to be mixing Widget's declaration with an anticipation of how clients will make use of the Widget interface.

First of all, this is not at all wrong - after all, the means of how the clients will (and can) use the interface is part of the interface itself; and for C++, not being garbage-collected, memory management of objects is a rather crucial part of their interface.

So there are two cases. In one case, the Widget would anticipate it would be used through a shared pointer. This would mean that eg. child widgets obtained from a widget are returned as shared_ptrs, everywidget created has it shared_ptr and so on. It would be totally legitimate to typedef WidgetPtr in the same header as Widget.

In the second case, Widgets would expect to be managed eg. by ordinary new and delete. The clients can use shared_ptrs in special cases, but nothing says eg. a printer dialogue routine can't use auto_ptr instead. The clients have to be prepared that if wptr is a shared_ptr, the line

shared_ptr<Widget> w2(wptr->firstChild()->parent());

leads to a disaster.

Your question seems to indicate the latter is your case. So IMHO, what you've done is OK. The clients can choose their means of managing Widget objects, as long as it doesn't affect other clients.

You are overthinking this in my opinion. Everybody who wants to have a shared_ptr<Widget> is going to have to include the Widget header file anyway. Putting the typedef (which is a good idea imo) in Widget.h makes 100% sense to me.

My approach (using underbar types, just because it is how I do it)

class Type
{
    public:
        typedef shared_ptr<Type>        ptr;
        typedef shared_ptr<const Type>  const_ptr;
};

I have found the const_ptr version is pretty darn useful.

I used to structure my C++ code into libraries. A library would have a bunch of headers for client consumption, all inside the include/LibraryName directory. Also, I would have one header called Fwd.h inside this directory with forward declarations of all classes along with their pointer typedefs.

In addition, each public header would include Fwd.h so that including the header would automatically give you all forward declarations and pointer typedefs. This worked really well in practice.

Not all classes are necessary to place in a shared_ptr though. I would only create pointer typedefs for types that I expected to be created dynamically, and in this case I would supply a factory. This has the added benefit that you may get away with supplying client code with interface types only, and hide concreted implementations in your library's src directory. Not specifically what you asked advice for, but this gives the complete picture of my method. As a final point, it's also possible to supply a convenience header called LibraryName.h that includes the Fwd.h and all other public headers.

Good luck!

I generally use this approach to ease typing and makes a generic shared pointer interface for classes. Do note it's C++0x.

#include <iostream>
#include <memory>

template <class T>
struct SharedVirtual
{
   typedef std::shared_ptr<T> VPtr;
};

template <class T>
struct Shared
{
   typedef std::shared_ptr<T> Ptr;

   template <class... P>
   static Ptr ptr(P&&... args) 
   { 
      return std::make_shared<T>(std::forward<P>(args)...); 
   }
};

class Foo : public SharedVirtual<Foo>
{
   public:
      virtual void foo() const = 0;
};

class Test : public Foo, public Shared<Test>
{
   public:
      void foo() const { std::cout << "Hai u!" << std::endl; }
};

void print(const Foo::VPtr& ptr)
{
   ptr->foo();
}

int main()
{
   auto ptr = Test::ptr();
   print(ptr);
}

Second part first: use a namespace, i.e.:

namespace WidgetStuff {
  class Widget { ..
  typedef shared_ptr<Widget> WidgetPtr;
  ..

If you want to split it up:

namespace WidgetStuff {
   class Widget { ...
}
...
namespace WidgetStuff { 
  typedef ...

You're the library author, you own the namespace, so no one else should invade it.

And now part one is answered too, if you choose you can do:

#include <widget.h>
#include <widget_utils.h>

by splitting up the namespace as above. The effect is no one has to use the utilities, whether or not they do they should not invade your namespace, so they're free to make WidgetPtr mean something else, as long as it isn't in your namespace.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!