ChatGPT解决这个技术问题 Extra ChatGPT

Why do C++ libraries and frameworks never use smart pointers?

I read in a few articles that raw pointers should almost never be used. Instead they should always be wrapped inside smart pointers, whether it's scoped or shared pointers.

However, I noticed that frameworks like Qt, wxWidgets and libraries like Boost never return nor expect smart pointers, as if they were not using them at all. Instead, they return or expect raw pointers. Is there any reason for that? Should I stay away from smart pointers when I write a public API, and why?

Just wondering why smart pointers are recommended when many major projects seem to avoid them.

All of those libraries you just named were started many years ago. Smart pointers only became truly standard in C++11.
smart pointers do have an overhead (reference counting etc)- that may be critical - in embedded/real time systems for example. IMHO - smart pointers are for lazy programmers. Also lots of APIs go for the lowest common denominator. I feel the flames licking around my feet as I type!
@EdHeal: The reason you can feel flames licking around your feet is because you're entirely wrong in every respect. For example, what overhead is there in unique_ptr? None whatsoever. Are Qt/WxWidgets targetted at embedded or real time systems? No, they're intended for Windows/Mac/Unix on a desktop- at most. Smart pointers are for programmers who want to get it correct.
Really, mobile phones are running Java.
Smart pointers only truly standard in C++11? What??? These things have been used for more than 20 years.

J
Jacob

Apart from the fact that many libraries were written before the advent of standard smart pointers, the biggest reason is probably the lack of a standard C++ Application Binary Interface (ABI).

If you’re writing a header-only library, you can pass around smart pointers and standard containers to your heart’s content. Their source is available to your library at compile time, so you rely on the stability of their interfaces alone, not of their implementations.

But because of the lack of standard ABI, you generally cannot pass these objects safely across module boundaries. A GCC shared_ptr is probably different from an MSVC shared_ptr, which too can differ from an Intel shared_ptr. Even with the same compiler, these classes are not guaranteed to be binary compatible between versions.

The bottom line is that if you want to distribute a prebuilt version of your library, you need a standard ABI on which to rely. C doesn’t have one, but compiler vendors are very good about interoperability between C libraries for a given platform—there are de facto standards.

The situation is not as good for C++. Individual compilers can handle interoperation between their own binaries, so you have the option of distributing a version for every supported compiler, often GCC and MSVC. But in light of this, most libraries just export a C interface—and that means raw pointers.

Non-library code should, however, generally prefer smart pointers over raw.


I agree with you, even passing a std::string can be a pain, This says a lot about C++ as a "great language for libraries".
The bottom line is more like: if you want to distribute a prebuild version you have to do so for every compiler you want to support.
@josefx: Yes this is sad but true, the only alternative is COM or a raw C interface. I wish the C++ comity would start worring for this kind of problems. I mean it's not like C++ is a new language from 2 years ago.
I downvoted because this is wrong. The ABI issues are more than managable in most cases. Whilst hardly user-friendly, ABI is also hardly insurmountable.
@NathanAdams: Such software is undoubtedly impressive and useful. But it treats the symptom of deeper problems: C++ semantics of lifetime and ownership are somewhere between impoverished and nonexistent. Those heap bugs would not have arisen if the language did not allow them. So sure, smart pointers are not a panacea—they’re an attempt to recoup some of the losses incurred by using C++ in the first place.
i
iammilind

There can be many reasons. To list few of them:

Smart pointers became part of standard just recently. Till then they were part of other libraries Their primary use is to avoid memory leaks; many libraries don't have their own memory management; Generally they provide utilities and APIs They are implemented as wrapper, since they are actually objects and not pointers. Which has additional time/space cost, compared to raw pointers; The users of the libraries may not want to have such overheads

Edit: Using smart pointers is a completely developer's choice. It depends on various factors.

In performance critical systems, you may not want to use smart pointers which generates overhead The project which needs the backward compatibility, you may not want to use the smart pointers which has C++11 specific features

Edit2 There is a string of several downvotes in the span of 24 hours because of below passage. I fail to understand why the answer is downvoted even though below is just an add-on suggestion and not an answer. However, C++ always facilitates you to have the options open. :) e.g.

template<typename T>
struct Pointer {
#ifdef <Cpp11>
  typedef std::unique_ptr<T> type;
#else
  typedef T* type;
#endif
};

And in your code use it as:

Pointer<int>::type p;

For those who say that a smart pointer and a raw pointer are different, I agree with that. The code above was just an idea where one can write a code which is interchangeable just with a #define, this is not compulsion;

For example, T* has to be deleted explicitly but a smart pointer does not. We can have a templated Destroy() to handle that.

template<typename T>
void Destroy (T* p)
{
  delete p;
}
template<typename T>
void Destroy (std::unique_ptr<T> p)
{
  // do nothing
}

and use it as:

Destroy(p);

In the same way, for a raw pointer we can copy it directly and for smart pointer we can use special operation.

Pointer<X>::type p = new X;
Pointer<X>::type p2(Assign(p));

Where Assign() is as:

template<typename T>
T* Assign (T *p)
{
  return p;
}
template<typename T>
... Assign (SmartPointer<T> &p)
{
  // use move sematics or whateve appropriate
}

On 3. Some smart pointers have additional time/space costs, others don't, including std::auto_ptr that has been part of the standard for a long time (and note, I do like std::auto_ptr as the return type for functions creating objects, even if it is almost useless everywhere else). In C++11 std::unique_ptr has no additional costs over a plain pointer.
Exactly... there is a nice symmetry on the appearance of unique_ptr and disappearance of auto_ptr, code targeting C++03 should use the later, while code targeting C++11 can use the former. Smart pointers are not shared_ptr, there are many standard and none standard, including proposals to the standard that were rejected as managed_ptr
@iammilind, those are interesting points, but the funny thing is that if we end up using smart pointers, as apparently many would recommend, we end up creating code incompatible with major libraries. Of course, we can wrap/unwrap the smart pointers as needed but it seems like a lot of hassle and would create inconsistent code (sometime we deal with smart pointers, sometime not).
The statement that smart pointers have "additional time/space cost" is somewhat misleading; all of the smart pointers except unique_ptr incur runtime cost, but unique_ptr is by far the one that is most commonly used. The code sample you provide is also misleading, because unique_ptr and T* are entirely different concepts. The fact that you refer to both of them as type gives the impression that they can be swapped out for one another.
You cannot typedef like that, these types are not in any way equivalent. Writing typedefs like this is asking for trouble.
M
Matthieu M.

There are two issues with smart pointers (pre C++11):

non-standards, so each library tend to reinvent its own (NIH syndrom & dependencies issues)

potential cost

The default smart pointer, in that it is cost-free, is unique_ptr. Unfortunately it requires C++11 move semantics, which only appeared recently. All other smart pointers have a cost (shared_ptr, intrusive_ptr) or have less than ideal semantics (auto_ptr).

With C++11 around the corner, bringing a std::unique_ptr, one would be tempted to think that it is finally over... I am not so optimistic.

Only a few major compilers implement most of C++11, and only in their recent versions. We can expect major libraries such as QT and Boost to be willing to retain compatibility with C++03 for a while, which somewhat precludes the wide adoption of the new and shiny smart pointers.


R
Robot Mess

You shouldn't stay away from smart pointers, they have their use especially in applications where you have to pass a object around.

Libraries tend to either just return a value or populate a object. They don't usually have objects that need to be used in a lot of places, so there is no need for them to use smart pointers (at least not in their interface, they may use them internally).

I could take as example a library we have been working on, where after a few months of development I realized we only used pointers and smart pointers in a few classes (3-5% of all classes).

Passing variables by reference was enough in most places, we used smart pointers whenever we had a object that could be null, and raw pointers when a library that we used forced us to.

Edit (I can't comment because of my reputation): passing variables by reference is very flexible: if you want the object to be readonly you can use a const reference (you can still do some nasty casts to be able to write the object) but you get the maximum of protection possible (it's the same with smart pointers). But I do agree that it's much nicer to just return the object.


I don't disagree, with you, exactly, but I will point out that there is a school of thought that deprecates the passing of variable references in most cases. I confess that I adhere to that school. I prefer functions not to modify their arguments. At any rate, as far as I know, C++'s variable references do nothing to prevent mishandling of the objects to which they refer, which is what smart pointers intend to do.
you have const for that (seems I can comment :D).
P
Puppy

Qt pointlessly re-invented many parts of the Standard library in an attempt to become Java. I believe that it does actually have its own smart pointers now, but in general, it is hardly a pinnacle of design. wxWidgets, as far as I'm aware, was designed long before usable smart pointers were written.

As for Boost, I fully expect that they use smart pointers wherever appropriate. You might have to be more specific.

In addition, don't forget that smart pointers exist to enforce ownership. If the API has no ownership semantics, then why use a smart pointer?


Qt was written before much of the functionality was sufficiently widespread on the platforms it wanted to use. It has had smart pointers for a long long time, and uses them to do the implicit sharing of resources in almost all Q* classes.
Every GUI library needlessly reinvents the wheel. Even strings, Qt has QString, wxWidgets has wxString, MFC has the horribly named CString. Isn't a UTF-8 std::string good enough for 99% of GUI tasks?
@Inverse QString was created when std::string wasn't around.
Check when qt was created and what smart pointers was available at that time.
t
thb

Good question. I don't know the specific articles to which you refer, but I have read similar things from time to time. My suspicion is that the writers of such articles tend to harbor a bias against C++-style programming. If the writer programs in C++ only when he must, then returns to Java or such as soon as he can, then he doesn't really share the C++ mindset.

One suspects that some or most of the same writers prefer garbage-collecting memory managers. I don't, but I think differently than they do.

Smart pointers are great, but they have to keep reference counts. The keeping of reference counts bears costs -- often modest costs, but costs nonetheless -- at runtime. There is nothing wrong with saving these costs by using bare pointers, especially if the pointers are managed by destructors.

One of the excellent things about C++ is its support for embedded-systems programming. The use of bare pointers is part of that.

Update: A commenter has correctly observed that C++'s new unique_ptr (available since TR1) does not count references. The commenter also has a different definition of "smart pointer" than I have in mind. He may be right about the definition.

Further update: The comment thread below is illuminating. All of it is recommended reading.


For a start, embedded-systems programming is a vast minority of all programming, and quite irrelevant. C++ is a general-purpose language. Secondly, shared_ptr keeps a reference count. There are many other smart pointer types which do not keep a reference count at all. Finally, the libraries mentioned are targetted at platforms which have plenty of resources to spare. Not that I was the downvoter, but all I'm saying is that your post is full of wrong.
@thb - I agree with you sentiment. DeadMG - Please try to live without embedded systems. Yes - some smart pointers do not have an overhead, but some do. The OP mentions libraries. Boost for example has parts that are used by embedded systems - but smart pointers may be inappropriate for certain applications.
@EdHeal: Not living without embedded systems != programming for them is not a tiny, irrelevant, minority. Smart pointers are appropriate for every situation in which you need to manage the lifetime of a resource.
shared_ptr has no overhead. It only has overhead if you don't need thread-safe shared ownership semantics, which is what it provides.
No, shared_ptr has significant overhead over the minimum necessary for thread-safe shared ownership semantics; specifically it allocates a heap block separate from the actual object you are sharing, for the sole purpose of storing the refcount. intrusive_ptr is more efficient, but (like shared_ptr) it also assumes that every pointer to the object will be an intrusive_ptr. You can get even lower overhead than intrusive_ptr with a custom ref-counting shared pointer, as I do in my app, and then use T* whenever you can guarantee that at least one smart pointer will outlive the T* value.
D
David C. Bishop

There are also other types of smart pointers. You might want a specialized smart pointer for something like network replication (one that detects if it's accessed and sends any modifications to the server or some such), keeps a history of changes, marks the fact that it was accessed so it can be investigated when you save data to disk and so on. Not sure if doing that in the pointer is the best solution but using the built in smart pointer types in libraries could result in people being locked into them and loosing the flexibility.

People can have all kinds of different memory management requirements and solutions beyond smart pointers. I might want to manage memory myself, I could be allocating space for things in a memory pool so it's allocated in advance and not at runtime (useful for games). I might be using a garbage collected implementation of C++ (C++11 makes this possible although none exist yet). Or maybe I'm just not doing anything advanced enough to worry about bothering with them, I can know that I'm not going to forget to uninitialized objects and so on. Maybe I'm just confident in my ability to manage memory without the pointer crutch.

Integration with C is another issue too.

Another issue is smart pointers are part of the STL. C++ is designed to be usable without the STL.


"Another issue is smart pointers are part of the STL." They are not.
s
slaphappy

It also depends on what domain you work in. I write game engines for a living, we avoid boost like the plague, in games the overhead of boost isn't acceptable. In our core engine we ended up writing our own version of stl (Much like the ea stl).

If i was to write a forms application, i might consider using smart pointers; but once memory management is second nature not having granular control over memory becomes quiet annoying.


There is no such thing as the "overhead of boost".
I've never had shared_ptr slow down my game engine to any noteworthy degree. They have sped up the production and debugging process though. Also, what exactly do you mean by "the overhead of boost?" That's a pretty large blanket to cast.
@curiousguy: It's the compilation overhead of all those headers and macro+template voodoo...