C++11 Features:
1. Rvalue references and move constructors
Move constructor: In addition to default, copy and copy assignment operator constructor c++11 introduced move constructor and move assignment operator constructor.
default, copy constructor, copy assignment operator constructors takes lvalue reference as a argument and just copy from source object to destination object means both the copies are present in source and destination objects. Where as in move constructor will take rvalue as a input and will move from source to destination object so source object empty after moving.
If user not implements the implicit defined move special member functions move their sub objects and data members in member wise.
2. constexpr – Generalized constant expressions
- not a virtual function
- return type and its param should be in literal
- constexpr must refere global variable.
- constexpr function can call only other constexpr function not simple function
- inside function not allow post, pre increment(++, --) expressions.
constexpr vs inline functions: inline means compiler to expand at at compile time, save time in function call overhead and evaluation done at run time, where as constexpr is evaluated at run time.
constexpr vs const: constexpr is mainly for optimization while const is for practically const objects like value of Pi(3,14).
valid, invalid places to use constexpr
3. Uniform initialization
- It allows the consistency syntax to initialize the variables and class objects, initialization uses {} open close curly braces. initializer more
- typename variable{arg1, arg2, arg3, ...}
Ex:
-. int x{}; //uninitialized built in type
- . int x{1}; //initialized built in type
-. int arr[]{1,2,3,4}; //aggregate initializing
-
int
4. Type inference (auto) auto
auto keyword can be used in place of variable type to tell the compiler infer the variable type from the variable type is called type inference/type deduction.
auto keyword is very useful to reducing the verbosity of the code.i.e
for (std::vector <int>::iterator it = v.begin(); it!= v.end(); ++it) can be written
for(auto it = v.begin(); it !=v.end(); ++it) // more readable as auto is simple to write
Notes:
- auto key work cannot be used with function parameters ex: int fun(auto x, auto y) // not allowed
- trailing return syntax Ex: auto func(int x, int y) -> int // it means return type is int
5. Range-based for loop range based for loop
easy iteration over the range of elements, STL containers contain begin(), end() range of elements will be iterate easily with range based for loop.
syntax: for(initialize starting value from range: range expression)
ex: for(auto it : vec) // here vec is vector and it is iterator will assigned form begin() to end () element.
for_each(): UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Sum{
public:
Sum():_sum(0){}
int _sum;
void operator()(int n){
_sum += n;
}
};
int main()
{
cout << "Hello World" << endl;
std::vector<int>vec{1,2,3,4,5,6,8};
auto f = [](const int n){
cout<<" "<<n;
};
auto f2 = [](int& n){
n++;
};
for_each(vec.begin(), vec.end(),f);
for_each(vec.begin(), vec.end(),f2);
cout<<"\n";
for_each(vec.begin(), vec.end(),f);
Sum s = for_each(vec.begin(), vec.end(),Sum());
cout<<"\n"<<s._sum;;
return 0;
}
6. Lambda functions and expressions
Anonymous / unnamed (no name to the function)/ lambda expressions are small snippet of code inside other functions.
Syntax :
7. Explicit overrides and final
Explicit: Explicit function specifier control the unwanted implicit type conversion.
- It can only be used in declaration of class constructor within the class declaration.
class abc{
public:
abc(){}
abc(int val) : m_val(val){}
private:
int m_val;
};
int main()
{
abc a=10;// compiler error
}
Override: Keyword override will make the compiler to check the base class to see if there is a virtual function with this exact signature. And if there is not, the compiler will show an error.
Final: final Sometimes you don’t want to allow derived class to override the base class’ virtual function. C++ 11 allows built-in facility to prevent overriding of virtual function using final specifier.
8. Null pointer constant (nullptr)
nullptr is a keyword that can be used at all places where NULL is expected. Like NULL, nullptr is implicitly convertible and comparable to any pointer type. Unlike NULL, it is not implicitly convertible or comparable to integral types
9. Strongly typed enumerations
enum classes / scoped enumeration which makes the enums strongly scoped and typed.
Ex:
10. Multithreading memory model
11. Thread-local storage
12. Explicitly defaulted and deleted special member functions
13. Static assertions
14. General-purpose smart pointers
- Shared_ptr will be used when shared ownership of resource needed
- It is a reference counting ownership model.
- Each shared pointer internally points to two memory locations i.e 1. pointer to the object 2. pointer to the control data that is used for reference counting.
Ex: shared_ptr<int>sptr(new int());
1. pointer to the int object, 2. memory of reference counting now count value is 1.
- raw pointer object will be deleted once the reference count becomes zero(0).
- sptr.use_count() will give the reference count value now that is 1 as per above sptr object as not shared to other pointers.
- custom delete as a function pointer we should pass when you make shared pointer scenarios like array of object shown below example
- custom delete can be lamda function, functor object as well example
- shared pointer spport only -> , *.
- shared pointer not support +, -, ++, --, [] operators.
int *ptr = new int();// raw pointer
*ptr = 10;
shared_ptr<int>sptr(ptr);//shared pointer from raw pointer
cout<<"\nsptr count "<<sptr.use_count()<<" "<<*sptr<<endl;
shared_ptr<int>p2(sptr);
shared_ptr<int>p3 = p2;// compiler error not allowed assignment
sptr.reset();//reset will decrease reference count by 1 and delete if count is 0.
cout<<"\n refene count"<<sptr.use_count()<<" p2 "<<p2.use_count()<<endl;
sptr.reset(new int[12]); it won't delete arra like delete [] so needed custom delete shown below
cout<<"\n refene count"<<sptr.use_count()<<" p2 "<<p2.use_count()<<endl;
void customDel(int x){
delete[] x; // to delete array [] required so custom delete needs to be written
}
shared_ptr<int>p(new int[12], customDel);
cout<<"\n refene count"<<sptr.use_count()<<" p "<<p.use_count()<<endl;
sptr = nullptr; // other way to reset using null pointer same count will decrease by 1 and delete if count is 0
cout<<"\n refene count"<<sptr.use_count()<<" p2 "<<p2.use_count()<<endl;
Notes for shared pointer:carefull
- Try not to use same raw pointer for more than one shared_ptr objects.
Ex: int *p = new int();
shared_ptr<int>sp1(p);{
shared_ptr<int>sp2(p);// wrong u not aware if sp2 goes out of scope then raw pointer deleted when sp1 goes out of scope then its a dangling pointer
}
- Don't create shared pointer from stack memory
Ex: int x= 10;
shared_ptr<int>spt2(&x); // &x is stack address when shared pointer goes out of scope then deleting stack memory using delete will crash the program
Week_ptr<>: weekpointer allows sharing but not owns the object i.e not maintain reference count.
- circular dependency of shared object problem can be solved using the week pointer
Ex: Class A, class B objects maintain the class object in each other then reference count never comes to 0 if we use shared pointer.
15. Extensible random number facility
16. Wrapper reference std::ref()
17. Uniform method for computing the return type of function objects
18. Lambdas
19. non-member begin() and end()
20.
1. Rvalue references and move constructors
Lvalue
|
R value
|
Exam
|
lvalue
is anything whose address is accessible. It means we can take address of
lvalue using & operator
|
Rvalue is anything
that is not lvalue. cannot take address of rvalue.
|
int x(lvalue) = 10(r
value);
int z (lvalue)
= (x+1) (r value);
here we cannot take
address of (x+1) but we can take z as it is l value int y =
&z;
|
It also doesn’t
persist beyond the single expression.
|
int getVal()
{
int val = 10;
return val; value of return is temporary as after it returns it won't persist
}
int y = getval();
getval is rvalue which will not get the address
|
|
Rvalues of built-in
data type is immutable
|
(x+1) = 10;
//compiler error cannot modify rvalue Getval()
= 8; // compiler error cannot modify rvalue
|
|
Rvalue of user
defined data type is not immutable
|
Class abc{
int x;
public:
abc() :x(0) {}
add(int val){ this.x +=val;}
}
abc getObj(){
return abc();
}
Int main()
{
abc * = & getObj(); //compiler
error
abc().add(10); // it increments value
but add method here is r value.
}
|
|
Lvalue reference
|
R value reference
|
|
A Reference
variable is an alias that always points to a an existing variable i.e
|
rvalue
references can do what lvalue references fails to do i.e. a rvalue reference
can refer to rvalues.
|
lvalue reference: int
x =10;
int &z = x;//
Works, but int &z = (x+2) //won’t work. As l value refers l values only.
rvalue reference:
int &&z=(x+2);
// is valid as now z is rvalue reference
with && is syntax
int &&y =
getval(); // also works
|
Move constructor: In addition to default, copy and copy assignment operator constructor c++11 introduced move constructor and move assignment operator constructor.
default, copy constructor, copy assignment operator constructors takes lvalue reference as a argument and just copy from source object to destination object means both the copies are present in source and destination objects. Where as in move constructor will take rvalue as a input and will move from source to destination object so source object empty after moving.
If user not implements the implicit defined move special member functions move their sub objects and data members in member wise.
Move constructor doesn’t allocate resources, shift the control of
memory and passed object assigned to NULL.
|
Move assignment operator first check the passed object and this
object then constructor doesn’t allocate resources, shift the control of
memory, delete passed object data pointers and passed object assigned to NULL.
|
abc::abc(abc&& obj);
|
abc& abc::operator=(abc&& obj)
|
abc::abc(abc &&obj)
{
m_data = obj.m_data; //move
obj.m_data = NULL;
//reset passed object data to null
}
|
abc& abc::operator=(abc &&obj)
{
If(&obj != this)
//check before move
m_data = obj.m_data;
//move
delete obj.m_data;
//delete passed object
obj.m_data = NULL;
//reset its object data to null
return *this; return new
moved object
}
|
2. constexpr – Generalized constant expressions
Constexpr feature is for performance improvement of program
by doing computations at compile time instead of run time.
Ex1: constexpr rand(int
x, int y) // here constexpr tells to compute the value at compile time.
{
return x+
y+20;
}
int val = rand(20, 30);
Ex2:
int arrsize1(int
x) constexpr
Int arrsize2(int x)
{
{
return x+1; return x+1;
}
}
int arr[arrsize1(20)];
//error as arrsize1() function will compute at run time
int arr[arrsize2(20)];
//error as arrsize2() function will compute at compile time bcz of constexpr
Note: constexpr function must follow below conditions- not a virtual function
- return type and its param should be in literal
- constexpr must refere global variable.
- constexpr function can call only other constexpr function not simple function
- inside function not allow post, pre increment(++, --) expressions.
constexpr vs inline functions: inline means compiler to expand at at compile time, save time in function call overhead and evaluation done at run time, where as constexpr is evaluated at run time.
constexpr vs const: constexpr is mainly for optimization while const is for practically const objects like value of Pi(3,14).
valid, invalid places to use constexpr
3. Uniform initialization
- It allows the consistency syntax to initialize the variables and class objects, initialization uses {} open close curly braces. initializer more
- typename variable{arg1, arg2, arg3, ...}
Ex:
-. int x{}; //uninitialized built in type
- . int x{1}; //initialized built in type
-. int arr[]{1,2,3,4}; //aggregate initializing
-
int
4. Type inference (auto) auto
auto keyword can be used in place of variable type to tell the compiler infer the variable type from the variable type is called type inference/type deduction.
auto keyword is very useful to reducing the verbosity of the code.i.e
for (std::vector <int>::iterator it = v.begin(); it!= v.end(); ++it) can be written
for(auto it = v.begin(); it !=v.end(); ++it) // more readable as auto is simple to write
Notes:
- auto key work cannot be used with function parameters ex: int fun(auto x, auto y) // not allowed
- trailing return syntax Ex: auto func(int x, int y) -> int // it means return type is int
5. Range-based for loop range based for loop
easy iteration over the range of elements, STL containers contain begin(), end() range of elements will be iterate easily with range based for loop.
syntax: for(initialize starting value from range: range expression)
ex: for(auto it : vec) // here vec is vector and it is iterator will assigned form begin() to end () element.
for_each(): UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Sum{
public:
Sum():_sum(0){}
int _sum;
void operator()(int n){
_sum += n;
}
};
int main()
{
cout << "Hello World" << endl;
std::vector<int>vec{1,2,3,4,5,6,8};
auto f = [](const int n){
cout<<" "<<n;
};
auto f2 = [](int& n){
n++;
};
for_each(vec.begin(), vec.end(),f);
for_each(vec.begin(), vec.end(),f2);
cout<<"\n";
for_each(vec.begin(), vec.end(),f);
Sum s = for_each(vec.begin(), vec.end(),Sum());
cout<<"\n"<<s._sum;;
return 0;
}
6. Lambda functions and expressions
Anonymous / unnamed (no name to the function)/ lambda expressions are small snippet of code inside other functions.
Syntax :
[ captureClause ] ( parameters ) -> returnType
{
statements;
}
{
statements;
}
capturescluse, parameters can be empty and return type is optional by
default return type treated as auto.
Ex: auto f = [] (){ cout<<"Hello Lamdas "<<endl}
The difference between the usage of functions and lambda functions boils down to two points:
- We cannot overload lambdas.
- A lambda function can capture local variables.
- A lambda should be short and concise.
- A lambda should be self-explanatory, especially since it does not have a name.
7. Explicit overrides and final
Explicit: Explicit function specifier control the unwanted implicit type conversion.
- It can only be used in declaration of class constructor within the class declaration.
class abc{
public:
abc(){}
abc(int val) : m_val(val){}
private:
int m_val;
};
int main()
{
abc a=10;// compiler error
}
Override: Keyword override will make the compiler to check the base class to see if there is a virtual function with this exact signature. And if there is not, the compiler will show an error.
Final: final Sometimes you don’t want to allow derived class to override the base class’ virtual function. C++ 11 allows built-in facility to prevent overriding of virtual function using final specifier.
8. Null pointer constant (nullptr)
nullptr is a keyword that can be used at all places where NULL is expected. Like NULL, nullptr is implicitly convertible and comparable to any pointer type. Unlike NULL, it is not implicitly convertible or comparable to integral types
9. Strongly typed enumerations
enum classes / scoped enumeration which makes the enums strongly scoped and typed.
Ex:
enum class Color // "enum class" defines this as a scoped enumeration instead of a standard enumeration
{
RED, // RED is inside the scope of Color
BLUE
};
enum class Fruit
{
BANANA, // BANANA is inside the scope of Fruit
APPLE
};
Color color = Color::RED; // note: RED is not directly accessible any more, we have to use Color::RED
Fruit fruit = Fruit::BANANA; // note: BANANA is not directly accessible any more, we have to use Fruit::BANANA
if (color == fruit) // compile error here, as the compiler doesn't know how to compare different types Color and Fruit
std::cout << "color and fruit are equal\n";
else
std::cout << "color and fruit are not equal\n";
10. Multithreading memory model
11. Thread-local storage
12. Explicitly defaulted and deleted special member functions
13. Static assertions
14. General-purpose smart pointers
Smart pointers are introduced to prevent the memory leaks
Unique_ptr<>:unique_ptr<>
objects wraps around the raw pointer and its responsible for its life time.
When these objects is destructed/out of scope then in its destructor it deletes
associated raw pointer.
- A unique_ptr object is always the unique owner of associated
raw pointer, we cannot copy only it’s a movable.
- It will be used when u want the single ownership of the
resource
- unique_ptr has its -> and * operator overloaded, so we
can use similar like normal pointers.
unique_ptr<int>uptr(new int);//unique pointer with raw pointer
*uptr = 100;
cout<<"pointer conent: "<<*uptr<<endl;
int *rptr = new int();
*rptr = 23;
unique_ptr<int>uptr1(rptr);//from existing raw pointer
cout<<"pointer conent: "<<*uptr1<<endl;
unique_ptr<int>emptuptr;//empty unique pointer
if(emptuptr == nullptr)
{
//reset will delete the associated raw pointer and unique_ptr make empty
uptr1.reset();// resetting the unique pointer
cout<<"unptr is empty if no raw pointer assined\n";
}
// unique_ptr<int>cuptr=uptr; //compilation error cannot assign
// emptuptr = upter;// compilation error cannot assign
unique_ptr<int>muptr = std::move(uptr);
if(uptr == nullptr)
cout<<"Empty uptr after move\n";
if(muptr != nullptr)
cout<<"Not an empty after move got ownership: "<<*muptr<<endl;
//relese
int *relptr = muptr.release(); //returns the associated raw pointer
cout<<"raw pointer content: "<<*relptr<<endl;
shared_ptr<>: is smart enough to automatically delete the associated pointer when its not used anywhere. Thus helps us to completely remove the problem of memory leaks and dangling Pointers. unique_ptr<int>uptr(new int);//unique pointer with raw pointer
*uptr = 100;
cout<<"pointer conent: "<<*uptr<<endl;
int *rptr = new int();
*rptr = 23;
unique_ptr<int>uptr1(rptr);//from existing raw pointer
cout<<"pointer conent: "<<*uptr1<<endl;
unique_ptr<int>emptuptr;//empty unique pointer
if(emptuptr == nullptr)
{
//reset will delete the associated raw pointer and unique_ptr make empty
uptr1.reset();// resetting the unique pointer
cout<<"unptr is empty if no raw pointer assined\n";
}
// unique_ptr<int>cuptr=uptr; //compilation error cannot assign
// emptuptr = upter;// compilation error cannot assign
unique_ptr<int>muptr = std::move(uptr);
if(uptr == nullptr)
cout<<"Empty uptr after move\n";
if(muptr != nullptr)
cout<<"Not an empty after move got ownership: "<<*muptr<<endl;
//relese
int *relptr = muptr.release(); //returns the associated raw pointer
cout<<"raw pointer content: "<<*relptr<<endl;
- Shared_ptr will be used when shared ownership of resource needed
- It is a reference counting ownership model.
- Each shared pointer internally points to two memory locations i.e 1. pointer to the object 2. pointer to the control data that is used for reference counting.
Ex: shared_ptr<int>sptr(new int());
1. pointer to the int object, 2. memory of reference counting now count value is 1.
- raw pointer object will be deleted once the reference count becomes zero(0).
- sptr.use_count() will give the reference count value now that is 1 as per above sptr object as not shared to other pointers.
- custom delete as a function pointer we should pass when you make shared pointer scenarios like array of object shown below example
- custom delete can be lamda function, functor object as well example
- shared pointer spport only -> , *.
- shared pointer not support +, -, ++, --, [] operators.
int *ptr = new int();// raw pointer
*ptr = 10;
shared_ptr<int>sptr(ptr);//shared pointer from raw pointer
cout<<"\nsptr count "<<sptr.use_count()<<" "<<*sptr<<endl;
shared_ptr<int>p2(sptr);
shared_ptr<int>p3 = p2;// compiler error not allowed assignment
sptr.reset();//reset will decrease reference count by 1 and delete if count is 0.
cout<<"\n refene count"<<sptr.use_count()<<" p2 "<<p2.use_count()<<endl;
sptr.reset(new int[12]); it won't delete arra like delete [] so needed custom delete shown below
cout<<"\n refene count"<<sptr.use_count()<<" p2 "<<p2.use_count()<<endl;
void customDel(int x){
delete[] x; // to delete array [] required so custom delete needs to be written
}
shared_ptr<int>p(new int[12], customDel);
cout<<"\n refene count"<<sptr.use_count()<<" p "<<p.use_count()<<endl;
sptr = nullptr; // other way to reset using null pointer same count will decrease by 1 and delete if count is 0
cout<<"\n refene count"<<sptr.use_count()<<" p2 "<<p2.use_count()<<endl;
Notes for shared pointer:carefull
- Try not to use same raw pointer for more than one shared_ptr objects.
Ex: int *p = new int();
shared_ptr<int>sp1(p);{
shared_ptr<int>sp2(p);// wrong u not aware if sp2 goes out of scope then raw pointer deleted when sp1 goes out of scope then its a dangling pointer
}
Ex: int x= 10;
shared_ptr<int>spt2(&x); // &x is stack address when shared pointer goes out of scope then deleting stack memory using delete will crash the program
Week_ptr<>: weekpointer allows sharing but not owns the object i.e not maintain reference count.
- circular dependency of shared object problem can be solved using the week pointer
Ex: Class A, class B objects maintain the class object in each other then reference count never comes to 0 if we use shared pointer.
15. Extensible random number facility
16. Wrapper reference std::ref()
17. Uniform method for computing the return type of function objects
18. Lambdas
19. non-member begin() and end()
20.
No comments:
Post a Comment