2

In C++ it's common to see things such as:

Header of class C

//== C.h ==//
#pragma once
#include "B.h"

class C
{
    B b;
};

Header of class B

//== B.h ==//
#pragma once
#include "A.h"

class B
{
    A a;
};

Header of class A

//== A.h ==//
#pragma once

class A
{

};

Because C.h includes B.h, and B.h includes A.h; C.h ends up knowing implementation details about A.h. In this case the headers are included only three levels deep, but in large projects including one header can quickly lead to the inclusion of hundreds of additional headers. Also resulting in long compile times.

In C++ 11 I could declare the headers as following:

Header of class C:

//== C.h ==//
#pragma once
#include <memory>

// proto
class B;

class C
{
    std::unique_ptr<B> b;
};

Header of class B

//== B.h ==//
#pragma once
#include <memory>

// proto
class A;

class B
{
    std::unique_ptr<A> a;
};

Header of class A

//== A.h ==//
#pragma once

class A
{

};

When replacing all members (class, struct) with smart pointers, I can include C.h without having to know the implementation of class B. Every cpp file now only has to know about it's members, without having to know about the memory layout of it's members.

My question would be:

  • Is it good design practice (a good idea) to replace all class members, that are either class and struct, with unique pointers? Are their additional pro's or cons ?
Enigma
  • 1,699
  • 10
  • 14
  • 2
    This is similar to the pimpl idiom. A downside is that there is now an indirection every time you want to access the member and your class layout is now effectively fragmented across memory. – Simple Feb 24 '14 at 13:04
  • Not entirely. pImpl splits a class into a private and public part and allows access to the private implementation through the public interface. Its also a compile firewall but somewhat different. – Enigma Feb 24 '14 at 13:09
  • This is not even a well formed code. you cannot instanciate unique_ptr with a default deleter if the full class declaration is not present as it would call delete on a forwarded type, that is undefined behavior. And i agree with @Simple, what you propose is nothing more than Pimpl. – galop1n Feb 24 '14 at 13:09
  • the unique_ptr will be instantiated inside the compile unit (cpp) which will include the corresponding header. – Enigma Feb 24 '14 at 13:10
  • @galop1n You don't need the complete type, otherwise it would be impossible to implement a pimpl using `unique_ptr`. But you need a destructor declaration. See [this related question](http://stackoverflow.com/questions/6012157/is-stdunique-ptrt-required-to-know-the-full-definition-of-t). – juanchopanza Feb 24 '14 at 13:21
  • @juanchopanza You need a full class declaration everywhere you invoke delete on an object. To use unique_ptr with pimpl, you need to provide a custom deleter and implement the operator() in a source file where the full declaration of the private implementation is known. The purpose of RAII and smart pointer is to remove the need to write a destructor by yourself… – galop1n Feb 24 '14 at 13:51
  • @galop1n Fine. But all you have to add to OP's code are the declarations `~C();` and `~B();` (plus definitions in the `.cpp` files obviously.) – juanchopanza Feb 24 '14 at 14:01

2 Answers2

4

Straight direct data members support copying by default, if their types support copying. And it's maximally efficient. If one feels that it's worth it to trade that for shorter build time, then why use C++, why not instead (then) just use a language that leverages such tradeoff maximally, such as Java or C#.

In other words, it appears to be an ungood idea.

For shorter build times while still using C++, consider faster machine or build server park.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
1

It's not a good idea to replace all members with pointers, as it would add an extra layer of dereferencing. A direct member is actually allocated within the containing object, so accessing it is only requires a fixed offset from the containing object's location.

If you hide everything behind pointers, you would need to access the pointer member, then dereference the pointer to access the data (which will be somewhere else in memory). It's a trivial overhead on an individual basis, but it would soon add up if the principle was expanded across an entire program.

Another issue to bear in mind is maintainability. If you store everything via pointers then you need to manually including appropriate construction and copying. That can lead to various problems on a large project, as it increases the likelihood of programmer error. By contrast, the compiler does a lot of that automatically if you're using direct members.

An alternative that's worth looking-up is the PIMPL design pattern (Private Implementation, or Pointer to Implementation). It's essentially a way of hiding the private details of a class inside its source file, with a view to reducing header dependencies.

Peter Bloomfield
  • 5,578
  • 26
  • 37