📖
Go C++
  • Introduction
  • Chapter 1: What You Must Know First
    • Virtual Address Space of Process: Memory Partition and Layout
    • Function Call: Stack Frame
    • Program Compiling and Linking
  • Chapter 2: C++ Basics Improvement
    • Default Parameters
    • Inline Function
    • Function Overloading
    • new and delete
    • const and Pointers
    • References in Detail
  • Chapter 3: Object-Oriented Principles
  • Class and Object
  • Constructor and Destructor
  • Shallow Copy and Deep Copy
  • Initializer List
  • Various Member Functions
  • Pointer to Class Members
  • Chapter 4: Template Programming
  • Function Templates
  • Class Templates
  • Memory Allocators
  • Chapter 5: Operator Overloading
    • Operator Overloading
    • Introduction to Iterators
    • Issues of Iterator Invalidation
    • More about new and delete
    • Overloading of new and delete: Object Pool
  • Chapter 6: Inheritance and Polymorphism
    • Look inside Inheritance
    • More about Inheritance
    • Virtual Functions, Static Binding and Dynamic Binding
    • More about Virtual Functions
    • Understanding Polymorphism
    • Abstract Classes
    • Frequently Asked Interview Questions: Polymorphism
  • Chapter 7: Multiple Inheritance
    • Virtual Inheritance and Virtual Base Classes
    • Diamond Problem
    • Four Kinds of Type Conversions
  • Chapter 8: Standard Template Library
    • Sequence Containers
    • Container Adaptors
    • Associative Containers
    • More about Iterators
    • Function Objects
    • Generic Algorithms, Binders and Lambda Expressions
  • Chapter 9: Object Optimization
    • Behind the Object
    • Optimizing Objects in Functions
    • Member Functions with Rvalue References
    • Move Semantics and Perfect Forwarding
  • Chapter 10: Smart Pointers
    • Smart Pointers
    • Smart Pointers without Reference Counting
    • Smart Pointers with Reference Counting
    • Custom Deleters
  • Chapter 11: Function Objects and Binders
    • More about Binders
    • Introduction to std::function
    • Template Specialization and Argument Deduction
    • More about std::function
    • std::bind(): A Simple Thread Pool
    • More about Lambda Expressions
  • Chapter 12: Multithreading
    • Important Features in C++11
    • Multithreaded Programming with std::thread
    • Mutual Exclusion
    • Producer-Consumer Problem
    • Atomic Operations
    • Thread Visibility and volatile
  • Chapter 13: Design Patterns
    • Singleton Pattern
    • Factory Pattern
    • Proxy Pattern
    • Decorator Pattern
    • Adapter Pattern
    • Observer Pattern
Powered by GitBook
On this page

Was this helpful?

  1. Chapter 9: Object Optimization

Member Functions with Rvalue References

In Chapter 5, we have implemented a class MyString that represents a string, in which its copy constructor and operator = is defined as

MyString::MyString(const MyString &other) {
  _pstr = new char[strlen(other._pstr) + 1];
  strcpy(_pstr, other._pstr);
}
​
MyString::MyString &operator=(const MyString &other) {
  if (this == &other) return *this;
  delete[] _pstr;
  _pstr = new char[strlen(other._pstr) + 1];
  strcpy(_pstr, other._pstr);
  return *this;
}

The former allocates memory for the new string, and copies the data from the old one. The latter first deletes the old string, and then does the copy.

Now suppose we have an interface getString() which takes a MyString object, does some modification and returns a new object. And in the main function, we pass s1 inside, and use s2 to receive the return object.

MyString getString(MyString &str) {
    const char* pstr = str.c_str();
    myString tmp(pstr);
    // Do something 
    return tmp;
}
​
int main() {
    MyString s1 = "aaa";
    MyString s2 = "bbb";
    s2 = getString(s1);
    return 0;
}

From the last chapter we've already known that three principles of optimizing objects should be applied. However in some circumstances, we cannot either returns a temporary object or receive the returned object by initialization, for example here. In this case, a temporary object has to be constructed on the heap of main() and be assigned to s2. What our copy constructor and operator = do here is to allocate new memory space, and copy the data from the temporary object. But since temporary object is destroyed immediately after then, it is an unnecessary overhead of memory. Why don't we just modify the pointer of the new object, and let it point to the temporary object's data? Fortunately, C++ 11 introduced Rvalue references to deal with these circumstances.

First let's review what is a Rvalue. We already know that a Lvalue has a specific name or a memory address, for example variables and objects. Rvalues are those without a name or memory address, for example, constants and temporary objects. We can only use a constant reference or a Rvalue reference to refer to a Rvalue:

int main() {
    const int &a = 20;
    int &&b = 20;
    int &&c = MyString("aaa");
}

Knowing this, we can add a new copy constructor which takes a Rvalue reference as the parameter. Inside the copy constructor, we only need to let the new pointer point to the data of the temporary object. After then, we set its pointer to NULL, so that the destructor does nothing.

MyString(MyString &&other) {
    _pstr = other._ptr;
    other._pstr = nullptr;
}

operator = with Rvalue reference can be added in a similar way:

MyString &operator=(MyString &&other) {
    if (this == &other) return *this;
    delete[] mptr;
    _pstr = other._pstr;
    other._pstr = null;
    return *this;
}

Now when the copy constructor or operator = is called on a temporary object, only the pointers are changed. No extra memory allocation or deallocation is needed, which increases memory efficiency. Member functions with Rvalue references are widely used in STL containers to improve performance.

PreviousOptimizing Objects in FunctionsNextMove Semantics and Perfect Forwarding

Last updated 4 years ago

Was this helpful?