for Robot Artificial Inteligence

5. Move semantics, Classes

|

intuition lvalues, rvalues

  • Every expression is an lvalue or an rvalue
  • lvalues can be written on the left of assignment operator (=)
  • rvalues are all the other expressions
  • Explicit rvalue defined using &&
  • Use std::move(…) to explicitly convert an
  • lvalue to an rvalue
    int a; // "a" is an lvalue
    int& a_ref = a; // "a" is an lvalue
                  // "a_ref" is a reference to an lvalue
    a = 2 + 2; // "a" is an lvalue ,
            // "2 + 2" is an rvalue
    int b = a + 2; // "b" is an lvalue , "a + 2" is an rvalue
    int&& c = std :: move(a); // "c" is an rvalue
    

Hands on example

#include <iostream>
#include <string>
using namespace std; // Save space on slides.
void Print(const string& str) {
  cout << "lvalue: " << str << endl;
}
void Print(string && str) {
  cout << "rvalue: " << str << endl;
}
int main () {
  string hello = "hi";
  Print(hello); // lvalue
  Print("world"); // lvalue
  Print(std :: move(hello)); // rvalue : more save memory and time
  // DO NOT access "hello" after move!
  return 0;
}

Never access values after move

  • 즉 Move를 하면 해당 variable에 value는 NULL이 되어 찾을 수 없다.
  • The value after move is undefined
#include <iostream>
#include <string>
#include <vector>
using namespace std; // Save space on slides.
int main () {
  string hello = "hello";
  vector <string > owner;
  owner. emplace_back (hello); // Copy.
  owner. emplace_back (move(hello)); // Move.
  cout << hello << endl; // Undefined.
  return 0;
}

How to think about std::move

  • Think about ownership
  • Entity(독립체) owns a variable if it deletes it, e.g.
    • A function scope owns a variable defined in it
    • An object of a class owns its data members
  • Moving a variable transfers ownership of its resources to another variable
  • When designing your program think “who should own this thing?”
  • Runtime: better than copying, worse than passing by reference

Custom operators for a class

  • Operators are functions with a signature:
    • operator()
  • represents the target operation, e.g. >, <, =, ==, << etc.
  • Have all attributes of functions
  • Always contain word operator in name
  • All available operators: http://en.cppreference.com/w/cpp/language/operators

Example operator <

#include <algorithm >
#include <vector >
using namespace std;
class Human{
public:
  Human(int kindness) : kindness_{kindness}{}
  bool operator <(const Human& other) const {
    return kindness_ < other. kindness_ ;
  }
private:
  int kindness_ = 100;
};
int main () {
  vector <Human > humans = {Human {0}, Human {10}};
  std :: sort(humans.begin (), humans.end ());
  return 0;
}

Copy constructor

  • Called automatically when the object is copied
  • For a class MyClass has the signature: MyClass(const MyClass& other)
MyClass a; // Calling default constructor.
MyClass b(a); // Calling copy constructor.
MyClass c = a; // Calling copy constructor.

Copy assignment operator

  • Copy assignment operator is called automatically when the object is assigned a new value from an Lvalue
  • For class MyClass has a signature: MyClass& operator=(const MyClass& other)
  • Returns a reference to the changed object
  • Use * this from within a function of a class to get a reference to the current object
MyClass a; // Calling default constructor.
MyClass b(a); // Calling copy constructor.
MyClass c = a; // Calling copy constructor.
a = b; // Calling copy assignment operator.

Move constructor

  • Called automatically when the object is moved
  • For a class MyClass has a signature: MyClass(MyClass&& other)
MyClass a; // Default constructors.
MyClass b(std :: move(a)); // Move constructor
MyClass c = std :: move(a); // Move constructor.

Move assignment operator

  • Called automatically when the object is assigned a new value from an Rvalue
  • For class MyClass has a signature: MyClass& operator=(MyClass&& other)
  • Returns a reference to the changed object
MyClass a; // Default constructors.
MyClass b(std :: move(a)); // Move constructor.
MyClass c = std :: move(a); // Move constructor.
b = std :: move(c); // Move assignment operator.

Example of Assignment operator

#include <iostream >
using std :: cout; using std :: endl;
class Hmm{
public:
  Hmm(){cout<<"default"<<endl;}
  Hmm(const Hmm& other){cout <<"copy"<<endl;}
  Hmm(Hmm&& other) {cout<<"move"<<endl;}
  Hmm& operator=(const Hmm& other){ //copy assignment operator
    cout<<"copy operator"<<endl; return * this;
  }
  Hmm& operator=(Hmm&& other){
    cout << "move operator"<<endl; return * this;
  }
}
int main () {
  Hmm a; // default constructor
  Hmm b = a; // copy contructor
  a = b; // copy operator
  Hmm c = std :: move(a); // move contructor
  c = std :: move(b); // move operator
  return 0;
}
  • 파라미터가 Class data 타입일 떄 항상 function 파라미터에 Reference & 를 붙인다.
  • Move일 경우에는 && Refenrence 두개를 붙인다.
  • copy operator일 경우에는 datatype& operator=(datatype&& other)이렇게 한다.

Do I need to define all of them?

  • The constructors and operators will be generated automatically
  • Under some conditions…
  • Five special functions for class MyClass:
    • ~MyClass()
    • MyClass(const MyClass& other)
    • MyClass(MyClass&& other)
    • MyClass& operator=(const MyClass& other)
    • MyClass& operator=(MyClass&& other)
  • None of them defined: all autogenerated
  • Any of them defined: none autogenerated

Rule of all or nothing

  • Try to define none of the special functions
  • If you must define one of them define all
  • Use =default to use default implementation
class MyClass {
  public:
    MyClass () = default;
    MyClass(MyClass && var) = default;
    MyClass(const MyClass& var) = default;
    MyClass& operator=( MyClass && var) = default;
    MyClass& operator=(const MyClass& var) = default;
};

Deleted functions

  • Any function can be set as deleted
    void SomeFunc (...) = delete;
    
  • Calling such a function will result in compilation error
  • Example: remove copy constructors when only one instance of the class must be guaranteed
  • Compiler marks some functions deleted automatically
  • Example: if a class has a constant data member, the copy/move constructors and assignment operators are implicitly deleted

Inheritance

  • Classes and structs can inherit data and functions from other classes
  • There are 3 types of inheritance in C++:
    • public [used in this course] GOOGLE-STYLE
    • protected
    • private
  • public inheritance keeps all access specifiers of the base class

Public inheritance

  • Public inheritance stands for “is a” relationship, i.e. if class Derived inherits publicly from class Base we say, that Derived is a kind of Base
    class Derived:public Base{
    // Contents of the derived class
    };
    };
    
  • Allows Derived to use all public and protected members of Base
  • Derived still gets its own special functions: constructors, destructor, assignment operators

Example 1 (inheritance)

#include <iostream>
using std::cout; using std::endl;
class Recatangle{
public:
  Recatangle(int width, int height) : width_{width}, height_(height)
  int weight() const {return width_;}
  int height() const {return height_;}
private:
  int width_=0;
  int height_=0;
};
class Square : public Recatangle{
public:
  explicit Square(int size) : Recatangle{size,size}{};
  // explicit is google style, inheritance가 있고 children일떄 사용한다.
};
int main(){
  Square sq(10); //short name to save space
  cout << sq.width()<< "" <<sq.height()<<endl;
  return 0;
}

Example 2

#include<iostream>
#include<string>
using std::cout; using std::endl;
class Printer{
public:
  Printer(const std::string& name){name_=name}
  void printer(){cout<<name_<<endl;}
private:
  std:string name_;
}
int main()
{
  std::string hello = "hi";
  Printer printer(hello)
  printer.Print()
  return 0;
}

Function overriding

  • A function can be declared virtual
    virtual Func(<PARAMS >); // assume in base class
    
  • If function is virtual in Base class it can be overridden in Derived class:
    Func(<PARAMS>) override; // assume in derived class
    
  • Base can force all Derived classes to override a function by making it pure virtual
    virtual Func(<PARAMS >) = 0;
    

Overloading vs overriding

  • Do not confuse function overloading and overriding
  • Overloading:
    • Pick from all functions with the same name, but different parameters
    • Pick a function at compile time
    • Functions don’t have to be in a class
  • Overriding:
    • Pick from functions with the same arguments and names in different classes of one class hierarchy
    • Pick at runtime

Abstract classes and interfaces

  • Abstract class: class that has at least one pure virtual function
  • Interface: class that has only pure virtual functions and no data members

How virtual works

  • A class with virtual functions has a virtual table
  • When calling a function the class checks which of the virtual functions that match the signature should be called
  • Called runtime polymorphism(多型现象)
  • Costs some time but is very convenient

Reference

https://www.ipb.uni-bonn.de/teaching/modern-cpp/

Cpp Core Guidelines: https://github.com/isocpp/CppCoreGuidelines

Git guide: http://rogerdudler.github.io/git-guide/

C++ Tutorial: http://www.cplusplus.com/doc/tutorial/

Book: Code Complete 2 by Steve McConnell

Modern CMake Tutorial https://www.youtube.com/watch?v=eC9-iRN2b04

Compiler Explorer: https://godbolt.org/ Gdbgui: https://www.gdbgui.com/ CMake website: https://cmake.org/ Gdbgui tutorial: https://www.youtube.com/watch?v=em842geJhfk

Fluent C++: structs vs classes: https://google.github.io/styleguide/cppguide.html#Structs_vs._Classes

Comments