- Copy initialization:
int numRings = 20;
- Direct initialization:
int numRings(20);
- Uniform initialization:
int numRings{20}
If uniform initialization is used with no value. Default is 0.
int numRings{}
Will give fixed sized integer in all architecture.
#include <cstdint> // for fixed width integers
/*
* 8 bit singed integer. Many systems consider them as chars. So it is better
* to not use them if using integers is the purpose
*/
int8_t var;
uint8_t var; // 8 bit unsigned integer
intN_t var; // N = 16, 32, 64 bits signed integer
uintN_t var; // N = 16, 32, 64 bits unsigned integer
Fixed width integers performance is machine dependent. To get the fastest
integer type on a specific machine use int_fastN_t
. It will give the fastest
integer which is at least N bits long. N = 8, 16, 32, 64.
int_fast32_t var;
To get the smallest integer which is at least N bits long use int_leastN_t
where N = 8, 16, 32, 64.
int_least64_t var;
Setting precision of a floating point number:
#include <iostream>
#include <iomanip> // for std::setprecision()
int main(){
double d{12.34567890};
std::cout << "Without precision: " << d << std::endl;
std::cout << std::setprecision(4);
std::cout << "With precision: " << d << std::endl;
return 0;
}
Special floating numbers: positive infinity, negative infinity, not a number.
#include <iostream>
int main(){
float zero(0.0);
float posinf = 5.0 / zero;
float neginf = -5.0 / zero;
float nan = zero / zero;
std::cout << posinf << std::endl;
std::cout << neginf << std::endl;
std::cout << nan << std::endl;
return 0;
}
#include <iostream>
int main(){
bool universeCameFromNothing(true);
std::cout << "Universe came from nothing: " << universeCameFromNothing
<< std::endl;
std::cout << std::boolalpha;
std::cout << "Universe came from nothing: " << universeCameFromNothing
<< std::endl;
return 0;
}
#include <iostream>
int main(){
char ch(65);
std::cout << ch << std::endl;
std::cout << static_cast<int>(ch) << std::endl;
return 0;
}
To get the variable or expression type.
#include <typeinfo>
int main(){
int numerator(50);
double denominator(5.0);
std::cout << typeid(numerator).name() << std::endl;
std::cout << typeid(numerator / denominator).name() << std::endl;
}
int integer(50); // integer
float f(0.05f); // used f suffix as default literal is double type
double d(0.31416); // double floating literal
int hex(0xa0); // hexadecimal literal
int oct(012); // Octal literal
int bin(0b1010); // Binary literal
int longNumber(1'23'570) // c++14 only
const double avg(6.023e23);
const double massEarth; // This is not allowed
int x(50);
const int y(x) // This is allowed
constexpr double adg(9.8); // Compile time constant. Value of the constant
// must be resolved in compile time otherwise
// will generate an error. This is c++11 feature
int number(5);
if(number == 5){
int number; // local variable
number = 10; // will effect only local variable
// this is shadowing
std::cout << number << std::endl; // will print local
}
std::cout << number << std::endl; // will print outer block number
::
is the global scope operator.
int x(50); // global variable
int main(){
int x(40);
std::cout << x << std::endl; // will print local x
std::cout << ::x << std::endl; // will print global x
}
Internal Variable: Can be used anywhere in the file they are defined but not out of the file.
External Variable: Cab be used across multiple files. Global variables are by default external. static keyword can be used to make them internal.
std::cin
is used for console input.std::cin
takes inputs untill first whitespace.
#include <iostream>
int main(){
int selection;
std::cin >> selection;
std::cout << "You have selected: " << selection << std::endl;
return 0;
}
std::getline()
is used to take whole line as input.
#include <iostream>
#include <string>
int main(){
std::string name;
std::cout << "Enter Name: ";
std::getline(std::cin, name);
std::cout << "Your name is: " << name;
return 0;
}
std::cin
takes upto newline from the input stream. So it will be a problem if any other input function is used after taking numeric input from std::cin.
#include <iostream>
#include <string>
int main(){
std::string name;
int select;
std::cout << "Select: ";
std::cin >> select;
std::cout << "Enter name: ";
std::getline(std::cin, name); // will not work
std::cout << "Your name is: " << name << "! You have selected: "
<< select;
return 0;
}
To solve this problem std::cin.ignore(n, ch)
can be used where n is the
number of character to ignore from the input stream before ch character is
found.
#include <iostream>
#include <string>
int main(){
int select;
std::string name;
std::cout << "Select: ";
std::cin >> select;
std::cin.ignore(32767, '\n');
std::cout << "Enter name: ";
std::getline(std::cin, name);
std::cout << "Hi " << name << "! You have selected " << select << std::endl;
return 0;
}
Expand to see code
#include <iostream>
double getDouble(){
double d;
while(true){
std::cout << "Enter: ";
std::cin >> d;
std::cin.ignore(32767, '\n'); /* clear '\n' from input stream */
/*
* Input will fail if a valid number isn't typed.
* if input fails, cin will set fail flag and stop extracting
* characters from input stream.
*/
if(std::cin.fail()){
std::cout << "Please enter a floating number" << std::endl;
std::cin.clear(); /* Clear fail flag */
std::cin.ignore(32767, '\n'); /* Clear input stream */
}
else{
return d;
}
}
}
char getOperator(){
char op;
while(true){
std::cout << "Enter (+, -, * or /): ";
std::cin >> op;
std::cin.ignore(32767, '\n');
if(op == '+' || op == '-' || op == '*' || op == '/'){
return op;
}
else{
std::cout << "Bad operator. Input again." << std::endl;
}
}
}
void printResult(double d1, char op, double d2){
if(op == '+'){
std::cout << d1 << " + " << d2 << " = " << d1 + d2 << std::endl;
}
else if(op == '-'){
std::cout << d1 << " - " << d2 << " = " << d1 - d2 << std::endl;
}
else if(op == '*'){
std::cout << d1 << " * " << d2 << " = " << d1 * d2 << std::endl;
}
else{
if(d2 != 0){
std::cout << d1 << " / " << d2 << " = " << d1 / d2 << std::endl;
}
else{
std::cout << "Can't devide by zero!";
}
}
}
int main(){
double d1 = getDouble();
char op = getOperator();
double d2 = getDouble();
printResult(d1, op, d2);
return 0;
}
#include <iostream>
#include <ctime>
#include <cstdlib>
int main(){
/* For generating different seed each time the program runs */
srand(static_cast<unsigned int>(time(0)));
std::cout << rand();
return 0;
}
Using modulus:
#include <iostream>
#include <ctime>
#include <cstdlib>
int getRandom(int min, int max){
return min + (rand() % (max - min + 1));
}
int main(){
/* For generating different seed each time the program runs */
srand(static_cast<unsigned int>(time(0)));
std::cout << getRandom(1, 6);
return 0;
}
But this method is biased to low numbers. Following method has less bias to low numbers.
#include <iostream>
#include <ctime>
#include <cstdlib>
int getRandom(int min, int max){
static const double fraction = 1.0 / RAND_MAX;
return min + ((max - min + 1) * (rand() * fraction));
}
int main(){
/* For generating different seed each time the program runs */
srand(static_cast<unsigned int>(time(0)));
std::cout << getRandom(1, 6);
return 0;
}
For details: http://www.learncpp.com/cpp-tutorial/59-random-number-generation/
Array index must be a compile time constant.
#include <iostream>
int main(){
int array[5]; // ok
#define ARR_SIZE 5
int array[ARR_SIZE]; // ok
int const arr_size = 5;
int array[arr_size]; // ok
int arr_size = 5;
}
Array index can be represented with enumerators. In this way it makes arrays well documented:
#include <iostream>
namespace Store{
enum Store{
LM7805,
MAX485,
LM311,
ATMEGA64,
LED,
MAX_ELEMENT
};
}
int main(){
int inStore[Store::MAX_ELEMENT];
inStore[Store::LM7805] = 50;
std::cout << "There are " << inStore[Store::LM7805] \
<< " LM7805 in store" << std::endl;
return 0;
}
Arrays are actually pointers. It points to the first element of the array.
#include <iostream>
int main(){
int arr[2] = {1, 2};
/* Following two address will be same*/
std::cout << arr << std::endl;
std::cout << &arr[0] << std::endl;
return 0;
}
The type of the array is int (*)[n]
if it is an integer array
but the type of the pointer to that array is int *
. Array type contains
the size information of the array. When array is dereferenced or assigned
to a pointer it implicitly converts itself into type *
from
type (*)[n]
so size information is lost. Here is an example of this
behaviour:
#include <iostream>
int main(){
int arr[5] = {1, 2, 3, 4, 5};
int *arrPtr = arr; // arr is converted from int (*)[2] to int *
/* Will print the size of the array which is 5 * 8 bytes */
std::cout << sizeof(arr) << std::endl;
/* Will print the size of the pointer which is 8 bytes */
std::cout << sizeof(arrPtr) << std::endl;
return 0;
}
#include <iostream>
int main(){
/*
* keep the string constant in memory with r/w access
* and return the pointer to it.
* So string constant can be changed any time later
*/
char arr[] = "hello, world";
/*
* Keep the string constant in read-only section of memory
* so it can't be changed
*/
char *text = "GNU is not Unix";
/* As it is a constant so its better to initialize following way */
const char *text = "GNU is not Unix";
arr[0] = 'g'; // This OK
/*
* In this case as string constant is kept in read-only
* memory, doing this will generate segmentation
* fault
*/
*(text + 2) = 'O';
std::cout << arr << std::endl;
std::cout << text << std::endl;
return 0;
}
From C++11 nullptr
keyword can be used to define a NULL pointer.
#include <iostream>
int main(){
int *ptr = nullptr;
if(ptr){
std::cout << "Not null" << std::endl;
}
else{
std::cout << "Null" << std::endl;
}
return 0;
}
- Can point to any data type
- Have to cast manually to a data type before dereferencing.
- Pointer arithmetic can't be done using void pointers as size of the obect isn't known
#include <iostream>
int main(){
int x(5);
void *ptr = &x; // pointing to an integer
std::cout << *static_cast<int*>(ptr) << std::endl;
char ch = 'P'; // pointing to a char
ptr = &ch;
std::cout << static_cast<char*>(ptr) << std::endl;
return 0;
}
Using reinterpret_cast<>
:
#include <iostream>
int main(){
int x = 17;
unsigned int addressX = reinterpret_cast<int>(&x);
std::cout << addressX << std::endl;
return 0;
}
There are three basic type of memory allocation:
-
Static memory allocation: Static and global variables. Allocated when program runs. Persist throught the program life. Memory is taken from the stack.
-
Atomatic memory allocation: Function parameter and local variables. Allocated when enter into relevent block and deallocated when exited the block. Memory is taken from the stack.
-
Dynamic memory allocation: Memory allocated and deallocated dynamically. Memory is taken from the heap.
Dynamically memory is allocated using the new
keyword.
#include <iostream>
int main(){
int *ptr = new int; // dynamically an integer is allocated.
*ptr = 5;
return 0;
}
Memory is deallocated using the delete
keyword.
#include <iostream>
int main(){
int *ptr = new int(5); // memory is allocated to the pointer
/* memory is deallocated.
* memory is realesed by os.
*/
delete ptr;
/* still the pointer is holding the memory address
* so its better to make it null
*/
ptr = 0;
ptr = nullptr; // c++11
return 0;
}
memory leak happens when allocated memory can't be deallocated.
#include <iostream>
void doSomthing(){
/* Memory is allocated but not deallocated
* so memory leak happens when the variable
* goes out of scope as there is no way
* to deallocate in that case
*/
int *ptr = new int;
}
int main(){
doSomthing();
return 0;
}
#include <iostream>
int main(){
int *ptr = int new;
int val;
// assigning to a address with out deallocating
ptr = &val; // memory leak
return 0;
}
#include <iostream>
int main(){
int *ptr = new int(5);
// allocating new memory without deallocating previous one
int *ptr = new int(10);
return 0;
}
Create alias for a variable. Basically share same memory address. Must be initialized with an addressable object. Can be used in function to pass parameter by reference.
#include <iostream>
int main(){
int x(10);
int &y = x; // reference variable
/*
* will output:
* 10
* 10
*/
std::cout << x << std::endl
std::cout << y << std::endl
return 0;
}
- Only works from c++11 above
- Can't be used in case of decayed arrays and dynamically allocated arrays.
#include <iostream>
int main(){
int fibseq[] = {1, 1, 2, 3, 5, 8, 13, 21};
std::cout << "Fibonacci Sequence: ";
for(const auto &elem: fibseq){
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
#include <iostream>
int foo(int x){
return x*x;
}
int main(){
int (*square)(int) = foo;
std::cout << square(10) << std::endl;
return 0;
}
- Can't be used for function's with default arguments
Ellipsis can be used to pass variable length argument in a function.
#include <iostream>
#include <cstdarg> // to use ellipsis
void printNum(int count, ...){
va_list list;
va_start(list, count);
for(int arg = 0; arg < count; arg++){
std::cout << va_arg(list, int) << std::endl;
}
va_end(list);
}
int main(){
printNum(5, 1, 2, 3, 4, 5);
return 0;
}
- Ellipsis are dangerous. Try to avoid them. For more - http://www.learncpp.com/cpp-tutorial/714-ellipsis-and-why-to-avoid-them/
To create anonymous functions. Simple example:
#include <iostream>
#include <algorithm>
#include <vector>
int main(int argc, char *argv[]) {
std::vector<bool> bitvect{1, 0, 0, 1};
/* lambda function example: used to make ~bitvect */
std::transform(bitvect.begin(), bitvect.end(), bitvect.begin(),
[](bool b){ return b == 1 ? 0 : 1; });
return 0;
}
Syntaxt:
[&](){}
: Capture all outside variable by reference.
[=](){}
: Capture all outside variable by value.
[&x](){}
: Capture only outside variable x
by reference.
[x](){}
: Capture only outside variable x
by value.
[&, x](){}
: Capture all outside variable by reference but x
by value.
[]() -> Type {}
: To specify return type.
#include <iostream>
class Point{
private:
double m_x;
double m_y;
public:
double getX(){
return m_x;
}
void setX(double x){
m_x = x;
}
double getY(){
return m_y;
}
void setY(double y){
m_y = y;
}
};
int main(){
Point p;
p.setX(-2.5);
p.setY(2.5);
std::cout << "(" << p.getX() << ", " << p.getY() << ")" << std::endl;
return 0;
}
#include <iostream>
class Point{
private:
double m_x;
double m_y;
public:
Point(double x = 0, double y = 0): m_x(x), m_y(y){
// empty
}
double getX(){
return m_x;
}
void setX(double x){
m_x = x;
}
double getY(){
return m_y;
}
void setY(double y){
m_y = y;
}
void printPoint(){
std::cout << "(" << m_x << ", " << m_y << ")" << std::endl;
}
};
int main(){
Point p(0.5, 0.5);
p.printPoint();
return 0;
}
In c++11 its possible to give default value in class memeber variable declaration.
#include <iostream>
class Point{
private:
double m_x = 0; // default value of x
double m_y = 0; // default value of y
public:
// member variable will be initialized with default value
// if called
Point(){
//empty
}
double getX(){
return m_x;
}
void setX(double x){
m_x = x;
}
double getY(){
return m_y;
}
void setY(double y){
m_y = y;
}
void printPoint(){
std::cout << "(" << m_x << ", " << m_y << ")" << std::endl;
}
};
int main(){
Point p; // will call default constructor
p.printPoint();
return 0;
}
#include <iostream>
#include <cassert>
class Point{
private:
double m_x;
double m_y;
public:
Point(double x = 0, double y = 0): m_x(x), m_y(y){
//empty
}
double getX(){
return m_x;
}
void setX(double x){
m_x = x;
}
double getY(){
return m_y;
}
void setY(double y){
m_y = y;
}
void printPoint(){
std::cout << "(" << m_x << ", " << m_y << ")" << std::endl;
}
};
class PointArray{
private:
Point *m_points;
int m_length;
public:
PointArray(int length){
m_points = new Point[length];
m_length = length;
}
// Destructor
~PointArray(){
delete[] m_points;
}
void insert(Point &p, int index){
assert(index >= 0 && index < m_length);
m_points[index] = p;
}
Point& get(int index){
assert(index >= 0 && index < m_length);
return m_points[index];
}
};
int main(){
PointArray parr(5);
Point p(2, 3);
Point q;
parr.insert(p, 0);
q = parr.get(0);
q.printPoint();
return 0;
}
- Will shared by all object.
- Not bound to any object. Bound to the whole class. So it is possible to use this variable without any object.
#include <iostream>
class Static{
public:
static int id;
};
/* Have to initialize first otherwise linker error will generate */
int Static::id = 1;
int main(){
Static s;
Static t;
/* Shared by both object */
s.id = 5;
std::cout << s.id << "\t" << t.id << std::endl;
/* Bound to whole class */
Static::id = 10;
std::cout << s.id << "\t" << t.id << std::endl;
return 0;
}
- Not bound to any object.
#include <iostream>
class ID{
private:
static int m_id;
public:
static int getID(){
return m_id;
}
};
/* Have to initialize first otherwise linker error will generate */
int ID::m_id = 1;
int main(){
std::cout << ID::getID << std::endl;
return 0;
}
In C++ classes can have memeber types or nested types. They make the class easy to maintain. For example in the following example it will be easy to change the type from int to double. It need to change in only one line.
#include <iostream>
class Point{
public:
using point_t = int; // Member type
Point(point_t x, point_t y): m_x(x), m_y(y){}
void print(void){
std::cout << "(" << m_x << ", " << m_y << ")";
}
private:
point_t m_x;
point_t m_y;
};
int main(int argc, char *argv[]){
Point p(10, 20);
p.print();
return 0;
}
public: private: protected:
#include <iostream>
class Point{
public:
using point_t = int;
Point(point_t x, point_t y): m_x(x), m_y(y){}
Point& add(point_t x, point_t y){
this->m_x += x;
this->m_y += y;
return *this;
}
Point& mul(point_t x, point_t y){
this->m_x *= x;
this->m_y *= y;
return *this;
}
void print(void){
std::cout << "(" << m_x << ", " << m_y << ")";
}
private:
point_t m_x;
point_t m_y;
};
int main(int argc, char *argv[]){
Point p(10, 20);
p.add(3, 5).mul(7, 8).add(2, 3);
p.print();
return 0;
}
C++ structs and classes are same. Only difference is that all members in structs are public.
Friend functions and classes can access the private members of a class. In the following example printWeather()
is friend of both Humidity
and Temperature
class. So it can access
private members of both classes.
#include <iostream>
class Humidity;
class Temperature
{
private:
int m_temp;
public:
Temperature(int temp=0) { m_temp = temp; }
friend void printWeather(const Temperature &temperature, const Humidity &humidity);
};
class Humidity
{
private:
int m_humidity;
public:
Humidity(int humidity=0) { m_humidity = humidity; }
friend void printWeather(const Temperature &temperature, const Humidity &humidity);
};
void printWeather(const Temperature &temperature, const Humidity &humidity)
{
std::cout << "The temperature is " << temperature.m_temp <<
" and the humidity is " << humidity.m_humidity << '\n';
}
int main()
{
Humidity hum(10);
Temperature temp(12);
printWeather(temp, hum);
return 0;
}
Classes can also be friend of another class. In following example Display
class if a friend
of Storage
class so it can access the private members.
#include <iostream>
class Storage
{
private:
int m_nValue;
double m_dValue;
public:
Storage(int nValue, double dValue)
{
m_nValue = nValue;
m_dValue = dValue;
}
// Make the Display class a friend of Storage
friend class Display;
};
class Display
{
private:
bool m_displayIntFirst;
public:
Display(bool displayIntFirst) { m_displayIntFirst = displayIntFirst; }
void displayItem(const Storage &storage)
{
if (m_displayIntFirst)
std::cout << storage.m_nValue << " " << storage.m_dValue << '\n';
else // display double first
std::cout << storage.m_dValue << " " << storage.m_nValue << '\n';
}
};
int main()
{
Storage storage(5, 6.7);
Display display(false);
display.displayItem(storage);
return 0;
}
In C++ each operator is actually a function. For example the operator +
is acturally
operator+()
function.
- At least one of the operand will have to be a custom type
#include <iostream>
class Point{
private:
using point_t = int;
point_t m_x;
point_t m_y;
public:
Point(): m_x(0), m_y(0){}
Point(point_t x, point_t y): m_x(x), m_y(y){}
// constant memeber function
void print(void) const {
std::cout << "(" << m_x << ", " << m_y << ")" << "\n";
}
// This is not a member function only a friend function
// This could defined outside of the function too
friend Point operator+(cost Point &p, const int n){
return Point(p.m_x + n, p.m_y + n);
}
friend Point operator+(const int n, const Point &p){
return p + n;
}
friend Point operator+(const Point &p1, const Point &p2){
return Point(p1.m_x + p2.m_x, p1.m_y + p2.m_y);
}
};
int main(int argc, char *argv[]){
Point p1(10, 20);
Point p2(4, 5);
Point p3 = p1 + p2 + 5;
p3.print();
return 0;
}
If there is no need to access private class data, operator overloading can be done as normal function.
#include <iostream>
class Point{
private:
using point_t = int;
point_t m_x;
point_t m_y;
public:
Point(): m_x(0), m_y(0){}
Point(point_t x, point_t y): m_x(x), m_y(y){}
point_t getX(void) const {
return m_x;
}
point_t getY(void) const {
return m_y;
}
// constant memeber function
void print(void) const {
std::cout << "(" << m_x << ", " << m_y << ")" << "\n";
}
};
Point operator+(const Point &p, const int n){
return Point(p.getX() + n, p.getY() + n);
}
Point operator+(int n, const Point &p){
return p + n;
}
Point operator+(const Point &p1, const Point &p2){
return Point(p1.getX() + p2.getX(), p1.getY() + p2.getY());
}
int main(int argc, char *argv[]){
Point p1(10, 20);
Point p2(4, 5);
Point p3 = p1 + p2 + 5;
p3.print();
return 0;
}
The assignment (=), subscript ([]), function call (()), and member selection (->) operators must be overloaded as member functions. IO operators(<< and >>) can't be overloaded as memeber functions.
#include <iostream>
class Point{
private:
using point_t = int;
point_t m_x;
point_t m_y;
public:
Point(): m_x(0), m_y(0){}
Point(point_t x, point_t y): m_x(x), m_y(y){}
// No need to pass the class explicitly as it will be passed as hidden this pointer
Point operator+ (int n){
return Point(m_x + n, m_y +n);
}
};
int main(int argc, char *argv[]){
Point p1(10, 20);
Point p2 = p1 + 5;
return 0;
}
#include <iostream>
class Point{
private:
using point_t = int;
point_t m_x;
point_t m_y;
public:
Point(): m_x(0), m_y(0){}
Point(point_t x, point_t y): m_x(x), m_y(y){}
point_t getX(void){
return m_x;
}
point_t getY(void){
return m_y;
}
friend std::ostream& operator<< (std::ostream &out, const Point &p);
friend std::istream& operator>> (std::istream &in, Point &p);
};
// Overloading as friend function
// Returning std::ostream so that it can be chained like std::cout << p1 << p2
std::ostream& operator<< (std::ostream &out, const Point &p){
out << "(" << p.m_x << ", " << p.m_y << ")";
return out;
}
std::istream& operator>> (std::istream &in, Point &p){
in >> p.m_x >> p.m_y;
return in;
}
// Overloading as normal function
Point operator+ (Point p, int n){
return Point(p.getX() + n, p.getY() + n);
}
Point operator+ (int n, Point p){
return p + n;
}
Point operator+ (Point p1, Point p2){
return Point(p1.getX() + p2.getX(), p1.getY() + p2.getY());
}
int main(int argc, char *argv[]){
Point p1(10, 20);
Point p2;
std::cout << "Enter a point:\n";
std::cin >> p2;
std::cout << p1 << " + " << p2 << " = " << p1 + p2 << "\n";
return 0;
}
When classes acts like function calls they are called functors. This can be done by overloading () operator.
#include <iostream>
class Point{
private:
using point_t = int;
point_t m_x;
point_t m_y;
public:
Point(): m_x(0), m_y(0){}
Point(point_t x, point_t y): m_x(x), m_y(y){}
point_t getX(void){
return m_x;
}
point_t getY(void){
return m_y;
}
// () can only be overloaded as member function
Point operator() (int n){
return Point(m_x + n, m_y + n);
}
friend std::ostream& operator<< (std::ostream &out, const Point &p);
};
// Overloading as friend function
// Returning std::ostream so that it can be chained like std::cout << p1 << p2
std::ostream& operator<< (std::ostream &out, const Point &p){
out << "(" << p.m_x << ", " << p.m_y << ")";
return out;
}
int main(int argc, char *argv[]){
Point pnt(10, 20);
// Will add 5 to pnt.m_x, pnt.m_y and create new Point object.
// Notce class object is called like function.
std::cout << pnt(5) << "\n";
return 0;
}
Typecast overloading can be done to convert between types.
#include <iostream>
#include <cmath>
class Polar{
private:
double m_r;
double m_theta;
public:
Polar(double r, double theta): m_r(r), m_theta(theta){}
friend std::ostream& operator<< (std::ostream &out, const Polar &p);
};
// Overloading as friend function
// Returning std::ostream so that it can be chained like std::cout << p1 << p2
std::ostream& operator<< (std::ostream &out, const Polar &p){
out << "(" << p.m_r << ", " << p.m_theta << ")";
return out;
}
class Cartesian{
private:
using point_t = int;
point_t m_x;
point_t m_y;
public:
Cartesian(): m_x(0), m_y(0){}
Cartesian(point_t x, point_t y): m_x(x), m_y(y){}
friend std::ostream& operator<< (std::ostream &out, const Cartesian &c);
// Typecast overloading
operator Polar() const {
double r = sqrt(pow(m_x, 2) + pow(m_y, 2));
double theta = atan(static_cast<double>(m_y) / static_cast<double>(m_x));
theta = (theta * 180) / M_PI;
return Polar(r, theta);
}
};
std::ostream& operator<< (std::ostream &out, const Cartesian &c){
out << "(" << c.m_x << ", " << c.m_y << ")";
return out;
}
int main(int argc, char *argv[]){
Cartesian cart(10, 20);
Polar pol(cart);
std::cout << "Cartesian: " << cart << "\tPolar: " << pol << "\n";
return 0;
}
When copy initialization is used a copy constructor is used. By default compiler uses memberwise initialization if no copy constructor is provided. For example:
class Xyz {
private:
int m_var;
public:
Xyz(int x): m_var(x){}
};
int main(void){
Xyz xy(10); // uses default initialization
Xyz yz(xy); // Copy initialization
// As there is no copy constructor provided, compiler will do a memberwise initialization
}
Member functions of a class can access the private members ot the same class type. Here is an example with copy constructor:
class Xyz {
private:
int m_var;
public:
Xyz(int x): m_var(x){}
// Copy constructor
Xyz(const Xyz &xyz): m_var(xyz.m_var){}
};
int main(void){
Xyz xy(10); // uses default initialization
Xyz yz(xy); // uses copy constructor
}
Copy initialization should be avoided as it can be elided for optimization.
#include <iostream>
#include <string>
class Hello{
private:
std::string m_s;
public:
Hello(std::string s): m_s(s){}
Hello(const Hello &h): m_s(h.m_s){
std::cout << "Copy constructor called\n";
}
std::string get(void){return m_s;}
};
int main(int argc, char *argv[]){
Hello h("hello");
Hello g(h); // Copy constructor will be used
Hello i = Hello("gello"); // Copy constructor won't be used
Hello k(Hello("gello")); // Copy constructor won't be used
std::cout << h.get() << g.get() << i.get() << k.get() << "\n";
}
If a class is passed by value in a function and return by value from a function copy constructor will be called. For example:
Hello changeHello(Hello h){ // Copy constructor will be called
h.change("new hello");
return h; // Copy constructor will be called
}
- explicit keyword can be used to prevent implicit conversion.
- delete keyword can be used to prevent both implicit and explicit conversion.
- For details
- Can only be oveloaded as memeber function.
- Watch out for self assignment.
- If no overloaded assignment operator is provided, compiler will do memberwise copy.
#include <iostream>
#include <string>
class Hello{
private:
std::string m_s;
public:
Hello(std::string s): m_s(s){}
Hello(const Hello &h): m_s(h.m_s){
std::cout << "Copy constructor called\n";
}
std::string get(void){return m_s;}
Hello& operator= (const Hello &h){
// If self copying
if(this == &h)
return *this; // for chainig
m_s = h.m_s;
return *this; // for chaining
}
};
int main(int argc, char *argv[]){
Hello h("hello");
Hello i("iello");
Hello j("jello");
Hello k(h); // Copy constructor is called
j = i = h; // Overloaded function is called
std::cout << h.get() << i.get() << j.get() << k.get() << "\n";
}
- Shallow copy means memberwise copying by the compiler if no copy constructor or overloaded assingment operator is provided.
- The default copy constructor and default assignment operators do shallow copies, which is fine for classes that contain no dynamically allocated variables.
- Classes with dynamically allocated variables need to have a copy constructor and assignment operator that do a deep copy.
In both cases relationship between parent and child is 'has a'.
-
The part (member) is part of the object (class)
-
The part (member) can only belong to one object (class) at a time
-
The part (member) has its existence managed by the object (class)
-
The part (member) does not know about the existence of the object (class)
-
Typically use normal member variables
-
Can use pointer members if the class handles object allocation/deallocation itself
-
Responsible for creation/destruction of parts
-
The part (member) is part of the object (class)
-
The part (member) can belong to more than one object (class) at a time
-
The part (member) does not have its existence managed by the object (class)
-
The part (member) does not know about the existence of the object (class)
-
Typically use pointer or reference members that point to or reference objects that live outside the scope of the aggregate class
-
Not responsible for creating/destroying parts
- The associated object (member) is otherwise unrelated to the object (class)
- The associated object (member) can belong to more than one object (class) at a time
- The associated object (member) does not have its existence managed by the object (class)
- The associated object (member) may or may not know about the existence of the object (class)
A dependency occurs when one object invokes another object’s functionality in order to accomplish some specific task. This is a weaker relationship than an association, but still, any change to object being depended upon may break functionality in the (dependent) caller. A dependency is always a unidirectional relationship.
Hold and organize multiple instance of another type(class or fundamental type).
Used in container class's constructor for list initialization.
Most base class constructed first and most derived class constructed last.
#include <iostream>
class Parent{
public:
Parent(){
std::cout << "A" << "\n";
}
};
class Child: public Parent{
public:
Child(){
std::cout << "B" << "\n";
}
};
int main(int argc, char *argv[]){
Child c;
return 0;
}
/*
* Will print:
* A
* B
*/
When a derived class is instanciated following things happen in order:
- Memory for derived is set aside (enough for both the Base and Derived portions)
- The appropriate Derived constructor is called
- The Base object is constructed first using the appropriate Base constructor. If no base constructor is specified, the default constructor will be used.
- The initialization list initializes variables
- The body of the constructor executes
- Control is returned to the caller
#include <iostream>
class Parent{
public:
int m_x;
Parent(int x=0): m_x(x){
std::cout << "A" << "\n";
}
};
class Child: public Parent{
public:
int m_y;
// Parent will be initialized with defautl vlaue
Child(int y=0): m_y(y){
std::cout << "B" << "\n";
}
// Parent will be initialized with given value
Child(int x, int y): Parent(x), m_y(y){
std::cout << "B" << "\n";
}
};
int main(int argc, char *argv[]){
// Parent's default constructor will be called
Child c(10);
// Parent will be initialized with given value
Child d(20, 10);
// Will print
// Parent 0
// Child 10
std::cout << "Parent " << c.m_x << "\nChild " << c.m_y << "\n";
// Will print
// Parent 20
// Child 10
std::cout << "Parent " << d.m_x << "\nChild " << d.m_y << "\n";
return 0;
}
When Child d(10, 20)
is called following things happended:
- Memory for Child is allocated.
- The Child(int, int) constructor is called, where x = 10, and y = 20
- The compiler looks to see if we’ve asked for a particular Base class constructor. We have! So it calls Parent(int) with x = 10.
- The base class constructor initialization list sets m_x to 10
- The base class constructor body executes, which prints A
- The base class constructor returns
- The derived class constructor initialization list sets m_y to 20
- The derived class constructor body executes, which prints B
- The derived class constructor returns
Destructors are called in reverse order of construction.
- public: Can be accessed by anybody.
- protected: Can be accessed by class member functions, friend functions and derived classes.
- private: Can be accessed by only class member functions and friend functions.
A summary of the behavious when inherited publicly, protectedly or privately:
Access Specifier in Base Class | Inherited Publicly | Inherited Protectedly | Inherited Privately |
---|---|---|---|
public | public | protected | private |
protected | protected | protected | private |
private | inaccessible | inaccessible | inaccessible |
#include <iostream>
class Base{
public:
Base(){}
void identify(void){
std::cout << "I am base\n";
}
};
class Derived: public Base{
public:
Derived(){}
void identify(void){
std::cout << "I am derived\n";
}
};
int main(int argc, char *argv[]){
Derived d;
// Will print I am derived
d.identify();
return 0;
}
- Redefined functions doesn't inherite access specification from parent.
#include <iostream>
class Base{
public:
Base(){}
void identify(void){
std::cout << "I am base\n";
}
};
class Derived: public Base{
public:
Derived(){}
void identify(void){
// if scope isn't used Derived::identify() will be called
Base::identify();
std::cout << "I am derived\n";
}
};
int main(int argc, char *argv[]){
Derived d;
// Will print
// I am base
// I am derived
d.identify();
return 0;
}
#include <iostream>
class Base{
public:
Base(){}
protected:
// Only membes, friends and derived class can call
void identify(void){
std::cout << "I am base\n";
}
};
class Derived: public Base{
public:
Derived(){}
// Base::identify() is now public
using Base::identify;
};
int main(int argc, char *argv[]){
Base b;
Derived d;
// Error: can't call from here
b.identify();
// OK: as it is public in Derived class
d.identify();
return 0;
}
Functionality can be hidden by making it private in derived class.
#include <iostream>
class Base{
public:
Base(){}
void identify(void){
std::cout << "I am base\n";
}
};
class Derived: public Base{
public:
Derived(){}
private:
// Base::identify() is now private
using Base::identify;
};
int main(int argc, char *argv[]){
Base b;
Derived d;
// OK: as it is public in Base class.
b.identify();
// OK: as it is public in Derived class
d.identify();
return 0;
}
- Ambiguity can arise.
#include <iostream>
class USBDevice
{
private:
long m_id;
public:
USBDevice(long id)
: m_id(id)
{
}
long getID() { return m_id; }
};
class NetworkDevice
{
private:
long m_id;
public:
NetworkDevice(long id)
: m_id(id)
{
}
long getID() { return m_id; }
};
class WirelessAdapter: public USBDevice, public NetworkDevice
{
public:
WirelessAdapter(long usbId, long networkId)
: USBDevice(usbId), NetworkDevice(networkId)
{
}
};
int main()
{
WirelessAdapter c54G(5442, 181742);
std::cout << c54G.getID(); // Which getID() do we call?
// Can be solved using scope
std::cout << c54G.USBDevice::getID();
return 0;
}
- Diamond problem
It is possible to create pointers to the base class of derived object. But the pointers won't be able to call member functions from the derived class.
#include <iostream>
class Base{
public:
Base(){}
void identify(void){
std::cout << "I am base\n";
}
};
class Derived: public Base{
public:
Derived(){}
void identify(void){
std::cout << "I am derived\n";
}
};
int main(int argc, char *argv[]){
Derived *pd = new Derived();
// Pointer to base of derived object
Base *pb{pd};
// Will call Derived::indentify()
pd->identify();
// Will call Base::identify()
pb->identify();
return 0;
}
Use case for this could be in functions which takes derived class as parameter. For each derived class a different functions have to be created. For example:
void report(Derived &d){
d.identify();
}
void report(AnotherDerived &ad){
ad.identify();
}
This problem can be solved using pointer/reference to base:
void report(Base &b){
b.identify();
}
int main(int argc, char *argv[]){
Derived d;
AnotherDerived ad;
report(d);
report(ad);
}
But the problem is in both cases Base::identify() will be called. As pointer to base only can see memebers from base. This problem can be solved using virtual functions.
A virtual function is a special type of function that, when called, resolves to the most-derived version of the function that exists between the base and derived class. This capability is known as polymorphism. A derived function is considered a match if it has the same signature (name, parameter types, and whether it is const) and return type as the base version of the function. Such functions are called overrides.
#include <iostream>
class Base{
public:
Base(){}
virtual void identify(void){
std::cout << "I am base\n";
}
};
class Derived: public Base{
public:
Derived(){}
void identify(void){
std::cout << "I am derived\n";
}
};
class AnotherDerived: public Derived{
public:
AnotherDerived(){}
void identify(void){
std::cout << "I am another derived\n";
}
};
int main(int argc, char *argv[]){
AnotherDerived ad;
Base *bp = &ad;
// Both resolve to AnotherDerived::identify()
ad.identify();
bp->identify();
Derived d;
bp = &d;
// Resolve to Derived::identity() as it is the most derived class in this case
bp->identify();
return 0;
}
Another example:
#include <iostream>
#include <string>
class Base{
public:
Base(){}
virtual std::string getName(void) const {return "Base";}
};
class Derived: public Base{
public:
Derived(){}
virtual std::string getName(void) const {return "Derived";}
};
class AnotherDerived: public Derived{
public:
AnotherDerived(){}
virtual std::string getName(void) const {return "AnotherDerived";}
};
void report(Base &b){
std::cout << "I am " << b.getName() << "\n";
}
int main(int argc, char *argv[]){
Derived d;
AnotherDerived ad;
report(d);
report(ad);
return 0;
}
If you want to call functions from base class in virtual function just use scope operator:
Derived d;
Base *bp = &d;
std::cout << bp->Base::getName() << "\n";
- Return type have to match between virtual functions.
- Never use virtual function in constructors and destructors.
A derived virtual function is only considered override if
functino signature and return type matches exactly between
the virtual functions. override
specifier enforces virtualization of a function.
If signature and return type doesn't match the compiler will
generate an error. If override
is used, no need to specify
virtual
keyword.
In case of inheritance destructors should always make virtual. Specially if there is dynamically allocated memory involved. If a derived class is converted to a base class and then call delete, only base destructor will be called. If dynamic memory is allocated in derived class, this will leak memory.
Abstract class is a class wich is only used by the derived class. It can't be instantiated anywhere else.
A pure virtual function has no body at all. It is meant to be redefined in derived classes. A class with pure virtual functions is an abstract class means it can't be intantiated. A pure virtual function may or may not have a body.
virtual void doSomething() = 0
In interface class has no member variables. All the functions are pure virtual.
- Single Base class is shared by the derived classes.
- Solves diamond problem.
- Most derived class is responsible for constructing the virtual base class.
Example:
#include <iostream>
class PoweredDevice
{
public:
PoweredDevice(int power)
{
std::cout << "PoweredDevice: " << power << '\n';
}
};
class Scanner: virtual public PoweredDevice // note: PoweredDevice is now a virtual base class
{
public:
Scanner(int scanner, int power)
: PoweredDevice{ power } // this line is required to create Scanner objects, but ignored in this case
{
std::cout << "Scanner: " << scanner << '\n';
}
};
class Printer: virtual public PoweredDevice // note: PoweredDevice is now a virtual base class
{
public:
Printer(int printer, int power)
: PoweredDevice{ power } // this line is required to create Printer objects, but ignored in this case
{
std::cout << "Printer: " << printer << '\n';
}
};
class Copier: public Scanner, public Printer
{
public:
Copier(int scanner, int printer, int power)
: PoweredDevice{ power }, // PoweredDevice is constructed here
Scanner{ scanner, power }, Printer{ printer, power }
{
}
};
Derived d;
Base b(d); // b will get the Base part from d. It is called object slicing
// Will call Base::doSomething() even if it is a virtual function
b.doSomething()
Base &b(d);
// Will call Derived::doSomething if it is a virtual function as b in reference to d
bp->doSomething()
- In general avoid slicing
Dynamic casing is used for downcasting.
Only member functions can be virtualized. So friend functions can't be virtualized. For example:
#include <iostream>
class Base
{
public:
Base() {}
// Here's our overloaded operator<<
friend std::ostream& operator<<(std::ostream &out, const Base &b)
{
// Delegate printing responsibility for printing to member function print()
return b.print(out);
}
// We'll rely on member function print() to do the actual printing
// Because print is a normal member function, it can be virtualized
virtual std::ostream& print(std::ostream& out) const
{
out << "Base";
return out;
}
};
class Derived : public Base
{
public:
Derived() {}
// Here's our override print function to handle the Derived case
virtual std::ostream& print(std::ostream& out) const override
{
out << "Derived";
return out;
}
};
int main()
{
Base b;
std::cout << b << '\n';
Derived d;
std::cout << d << '\n'; // note that this works even with no operator<< that explicitly handles Derived objects
Base &bref = d;
std::cout << bref << '\n';
return 0;
}
#include <iostream>
template <typename T>
T add(T x, T y){
return x + y;
}
int main(int argc, char *argv[]){
std::cout << add(1, 2) << "\n";
std::cout << add(1.5, 2.1) << "\n";
return 0;
}
For more than one type:
template <typename T1, typename T2>
void doSomething(T1 x, T1 y){
}
#include <iostream>
#include <string>
template <class T>
class Value{
private:
T m_x;
public:
Value(T x): m_x(x){}
T get(void){return m_x;}
};
int main(int argc, char *argv[]){
Value<int> ival(10);
Value<double> dval(10.25);
Value<std::string> sval("hello");
std::cout << "ival: " << ival.get() << " dval: " << dval.get() << " sval: " << sval.get()
<< "\n";
return 0;
}
If template class definition and implementation are splitted into seperate files linker error can be generated. To solve this following can be done:
- Definition and implementation in one file.
- Implementation in
*.inl
file and#include
in*.h
file.
For more details
#include <iostream>
template <class T, int size> // size is the non-type parameter
class StaticArray
{
private:
// The non-type parameter controls the size of the array
T m_array[size];
public:
T* getArray();
T& operator[](int index)
{
return m_array[index];
}
};
// Showing how a function for a class with a non-type parameter is defined outside of the class
template <class T, int size>
T* StaticArray<T, size>::getArray()
{
return m_array;
}
int main()
{
// declare an integer array with room for 12 integers
StaticArray<int, 12> intArray;
// Fill it up in order, then print it backwards
for (int count=0; count < 12; ++count)
intArray[count] = count;
for (int count=11; count >= 0; --count)
std::cout << intArray[count] << " ";
std::cout << '\n';
// declare a double buffer with room for 4 doubles
StaticArray<double, 4> doubleArray;
for (int count=0; count < 4; ++count)
doubleArray[count] = 4.4 + 0.1*count;
for (int count=0; count < 4; ++count)
std::cout << doubleArray[count] << ' ';
return 0;
}
If an exception is needed to make for an specific type.
#include <iostream>
#include <string>
#include <cstring>
char str[100];
template <class T>
T add(T x, T y){
return x + y;
}
// If it is an const char*
template<>
const char *add(const char *s1, const char *s2){
strcat(str, s1);
strcat(str, s2);
return str;
}
int main(int argc, char *argv[]){
std::cout << add(1, 2) << "\n";
std::cout << add(1.5, 2.2) << "\n";
std::cout << add("hello", "world") << "\n";
return 0;
}
decltype(s)
- Query the type of s