Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enforce no bald pointers in C++

Generally speaking, in this crazy modern world full of smart pointers, I'm coming round to the fact that bald/bare pointers shouldn't be in method signatures.

I suppose there might be some cases where you need bald pointers for performance reasons but certainly I've always managed with references or references to smart pointers.

My question is this, can anyone suggest an automated way to enforce this? I can't see a way with clang, GCC or vera++

like image 837
HNJSlater Avatar asked Jan 20 '26 15:01

HNJSlater


1 Answers

I think it is ok for non-owning raw-pointers to be used in method signatures. It is not ok to use "owning" raw-pointers.

For example this is quite ok:

void bar(const Widget* widget) {
  if (widget)
    widget->doSomething();
}

void foo() {
  Widget w;
  bar(&w);
}

If you want the Widget to be nullable you can't use a reference and using a smart pointer would be slower and not express the ownership model. The Widget can be guaranteed to be alive for the whole scope of foo. bar simply wants to "observe" it so a smart pointer is not needed. Another example where a non-owning raw-pointer is appropriate is something like:

class Node {
 private:
  std::vector<std::unique_ptr<Node>> children_;  // Unique ownership of children
  Node* parent_;
 public:
  // ...
};

The Node "owns" its children and when a Node dies its children die with it. Providing encapsulation of Node hasn't been broken, a Node can guarantee its parent is alive (if it has one). So a non-owning raw-pointer is appropriate for a back reference to the parent. Using smart pointers where they are not required makes code harder to reason about, can result in memory leaks and is premature pessimization.

If smart pointers are used throughout instead, all kinds of things can go wrong:

// Bad!
class Node {
 private:
  std::vector<std::shared_ptr<Node>> children_; // Can't have unique ownership anymore
  std::shared_ptr<Node> parent_; // Cyclic reference!
 public:

  // Allow someone to take ownership of a child from its parent.
  std::shared_ptr<Node> getChild(size_t i) { return children_.at(i); }
  // ...
};

Some of these problems can be solved by using weak_ptr instead of shared_ptr but they still need to be used with care. A weak_ptr is a pointer that has the option of owning.

The point is to use the appropriate kind of pointer in the appropriate context and to have an ownership model that is as simple as possible.

And no, I don't think there is an automated way of enforcing this, it is part of the design. There are tricks you can use like Cheersandhth.-Alf said to try and enforce a particular class is used in the way you want by restricting access.

like image 123
Chris Drew Avatar answered Jan 23 '26 08:01

Chris Drew