0

I am currently learning about C++. I learnt about some special member functions which are generated by the compiler.

• Default constructor. • Copy constructor • Move Constructor • Copy assignment operator • Move assignment operator • Destructor

I know about the functionalities of each member function. But I wanna know which of these member function are necessary and should be declared (instead of being generated by the compiler), as a part of good coding practices, keeping memory and efficiency in mind.

kinny94
  • 376
  • 1
  • 5
  • 22
  • 2
    Ideally you should not need to define anything other than a regular constructor (if needed). Good reference: http://en.cppreference.com/w/cpp/language/rule_of_three – NathanOliver Mar 31 '17 at 19:11
  • It all depends on what your class (type) does and what it contains. There is no generic answer. – Jesper Juhl Mar 31 '17 at 19:15
  • 90% of the time you won't define any of these. The other 10%, define the ones that make sense. If you want some code to run on destruction, define the destructor. There are a set of conditions when each of these is or isn't implicitly defined that you should know. – David Mar 31 '17 at 19:21

3 Answers3

1

But I wanna which of these member function are necessary and should be declared (instead of being generated by the compiler)

If the implicitly generated special member functions do what you intend, then none of them should be declared explicitly.

If some of the otherwise generated special member functions do not do what you intend, then you must declare them explicitly, and provide the implementation that you need.

If you have an explicit non-trivial definition for any of [destructor, copy/move constructor/assignment], then as a rule of thumb, you probably need a definition for each of them.

eerorika
  • 232,697
  • 12
  • 197
  • 326
1

The default constructor is a special case among these. If some member variables need an initial state that is not the default value of those members, write a default constructor.

The rest of these should really only be overwritten if you know that you need it.

These are some reasonable cases in which you might want to write custom versions of the listed functions:

  1. Your class has member variables that are pointers to dynamic memory that it has allocated.
  2. Your class has data structures as members that the user is not expected to pass completed versions of into the constructor.
  3. Your class has members that you do not want to use the default copy model for (e.g. you want a deep copy but the copy constructor or assignment would make a shallow copy).
  4. Your class has a static data member that should be changed when new instances of it are created.

Examples of each of these use cases include:

  1. STL vectors, which dynamically allocates its underlying array and deallocates in its destructor.
  2. STL lists, which copy data that is added to them onto list nodes, the internals of which are not exposed to the end-user.
  3. Any STL container class is a good example of this.
  4. Something where the number of things which need access to an expensive resource are needed. Similar to the design goal of a std::shared_ptr.

If one class meets too many of these requirements, you should probably consider changing your design to use more of the containers an utilities provided by the standard library. Especially if one class requires the fourth property and any of the other three. The responsibilities related to points 1-3 and point 4 should be handled by different classes. It is important to note that ownership of one large, expensive-to-create resource and ownership of many resources are different types of responsibility.

References:

Quinn Mortimer
  • 671
  • 6
  • 14
0

The default constructor is sort of the odd man out here. Whether or not you have a default construct doesn't necessarily have any relationship to whether you write the others (although, writing a move constructor without a default constructor can be hard in practice).

In most cases you should try to follow the Rule of Zero (http://en.cppreference.com/w/cpp/language/rule_of_three, http://www.nirfriedman.com/2015/06/27/cpp-rule-of-zero/), which means that you would not write any of these member functions for your class. Instead, choose members that correctly express the ownership semantics you want for various resources. A member std::vector correctly handles copying and movement the way you would want, so a class with such a member wouldn't necessarily need any special members. If you tried to use a raw pointer that was created with new[], however, as an array, then you would have many of these issues to deal with, and would need to write special member functions.

If you need to write one, you should explicitly think about all of them, but it doesn't mean you have to write all of them. Sometimes you may want to =default or =delete some of them. A quick example:

template <class T>
copy_ptr {
  copy_ptr() = default;
  copy_ptr(copy_ptr&&) = default;
  copy_ptr& operator=(copy_ptr&&) = default;

  copy_ptr(const copy_ptr& other)
    : m_resource(make_unique<T>(*other.m_resource)
 {}

 copy_ptr& operator=(const copy_ptr& other) {
   m_resource = make_unique<T>(*other.m_resource);
 }
 // define operator*, etc

  std::unique_ptr<T> m_resource;
};

This is a pointer, that instead of being uncopyable like unique_ptr, makes a deep copy of the object that it holds. However, we were able to use the default constructor, move operators, and destructors "as-is" from unique_ptr, so we defaulted them. We only rewrote the copy operations, doing the minimum work.

Basically, the idea with special operators in C++ is to push them "downwards", into smaller objects/classes. Which are typically solely responsible for managing that resource. Bigger classes that coordinate business logic should try very hard to defer resource management concerns to members.

Nir Friedman
  • 17,108
  • 2
  • 44
  • 72