5. Move semantics, Classes
30 May 2020 | Modern C++
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
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