abstract class
struct Base { virtual int g(); virtual ~Base() {} };
struct A : Base {
// OK: declares three member virtual functions, two of them pure
virtual int f() = 0, g() override = 0, h();
// OK: destructor can be pure too
~A() = 0;
// Error: pure-specifier on a function definition
virtual int b()=0 {}
};
struct Abstract {
virtual void f() = 0; // pure virtual
virtual void g() {} // non-pure virtual
~Abstract() {
g(); // OK: calls Abstract::g()
// f(); // undefined behavior
Abstract::f(); // OK: non-virtual call
}
};
// definition of the pure virtual function
void Abstract::f() { std::cout << "A::f()\n"; }
struct Concrete : Abstract {
void f() override {
Abstract::f(); // OK: calls pure virtual function
}
void g() override {}
~Concrete() {
g(); // OK: calls Concrete::g()
f(); // OK: calls Concrete::f()
}
};
override specifier
struct A
{
virtual void foo();
void bar();
};
struct B : A
{
void foo() const override; // Error: B::foo does not override A::foo
// (signature mismatch)
void foo() override; // OK: B::foo overrides A::foo
void bar() override; // Error: A::bar is not virtual
};
final specifier
struct Base
{
virtual void foo();
};
struct A : Base
{
void foo() final; // Base::foo is overridden and A::foo is the final override
void bar() final; // Error: bar cannot be final as it is non-virtual
};
struct B final : A // struct B is final
{
void foo() override; // Error: foo cannot be overridden as it is final in A
};
struct C : B // Error: B is final
{
};
friend declaration
class Y {
int data; // private member
// the non-member function operator<< will have access to Y's private members
friend std::ostream& operator<<(std::ostream& out, const Y& o);
friend char* X::foo(int); // members of other classes can be friends too
friend X::X(char), X::~X(); // constructors and destructors can be friends
};
// friend declaration does not declare a member function
// this operator<< still needs to be defined, as a non-member
std::ostream& operator<<(std::ostream& out, const Y& y)
{
return out << y.data; // can access private member Y::data
}
class F {};
int f();
int main()
{
extern int g();
class Local { // Local class in the main() function
friend int f(); // Error, no such function declared in main()
friend int g(); // OK, there is a declaration for g in main()
friend class F; // friends a local F (defined later)
friend class ::F; // friends the global F
};
class F {}; // local F
}
Bit field
#include <iostream>
struct S {
// will usually occupy 2 bytes:
// 3 bits: value of b1
// 2 bits: unused
// 6 bits: value of b2
// 2 bits: value of b3
// 3 bits: unused
unsigned char b1 : 3, : 2, b2 : 6, b3 : 2;
};
int main()
{
std::cout << sizeof(S) << '\n'; // usually prints 2
}
#include <iostream>
struct S {
// will usually occupy 2 bytes:
// 3 bits: value of b1
// 5 bits: unused
// 6 bits: value of b2
// 2 bits: value of b3
unsigned char b1 : 3;
unsigned char :0; // start a new byte
unsigned char b2 : 6;
unsigned char b3 : 2;
};
int main()
{
std::cout << sizeof(S) << '\n'; // usually prints 2
}
If the specified size of the bit field is greater than the size of its type, the value is limited by the type: a std::uint8_t b : 1000; would still hold values between 0 and 255. the extra bits become unused padding.
This Pointer
class T
{
int x;
void foo()
{
x = 6; // same as this->x = 6;
this->x = 5; // explicit use of this->
}
void foo() const
{
// x = 7; // Error: *this is constant
}
void foo(int x) // parameter x shadows the member with the same name
{
this->x = x; // unqualified x refers to the parameter
// 'this->' required for disambiguation
}
int y;
T(int x) : x(x), // uses parameter x to initialize member x
y(this->x) // uses member x to initialize member y
{}
T& operator= ( const T& b )
{
x = b.x;
return *this; // many overloaded operators return *this
}
};
class Outer {
int a[sizeof(*this)]; // error: not inside a member function
unsigned int sz = sizeof(*this); // OK: in default member initializer
void f() {
int b[sizeof(*this)]; // OK
struct Inner {
int c[sizeof(*this)]; // error: not inside a member function of Inner
};
}
}
Constructors and member initializer lists
#include <fstream>
#include <string>
#include <mutex>
struct Base {
int n;
};
struct Class : public Base
{
unsigned char x;
unsigned char y;
std::mutex m;
std::lock_guard<std::mutex> lg;
std::fstream f;
std::string s;
Class ( int x )
: Base { 123 }, // initialize base class
x ( x ), // x (member) is initialized with x (parameter)
y { 0 }, // y initialized to 0
f{"test.cc", std::ios::app}, // this takes place after m and lg are initialized
s(__func__), //__func__ is available because init-list is a part of constructor
lg ( m ), // lg uses m, which is already initialized
m{} // m is initialized before lg even though it appears last here
{} // empty compound statement
Class ( double a )
: y ( a+1 ),
x ( y ), // x will be initialized before y, its value here is indeterminate
lg ( m )
{} // base class initializer does not appear in the list, it is
// default-initialized (not the same as if Base() were used, which is value-init)
Class()
try // function-try block begins before the function body, which includes init list
: Class( 0.0 ) //delegate constructor
{
// ...
}
catch (...)
{
// exception occurred on initialization
}
};
int main() {
Class c;
Class c1(1);
Class c2(0.1);
}
Default constructors
struct A
{
int x;
A(int x = 1): x(x) {} // user-defined default constructor
};
struct B: A
{
// B::B() is implicitly-defined, calls A::A()
};
struct C
{
A a;
// C::C() is implicitly-defined, calls A::A()
};
struct D: A
{
D(int y): A(y) {}
// D::D() is not declared because another constructor exists
};
struct E: A
{
E(int y): A(y) {}
E() = default; // explicitly defaulted, calls A::A()
};
struct F
{
int& ref; // reference member
const int c; // const member
// F::F() is implicitly defined as deleted
};
int main()
{
A a;
B b;
C c;
// D d; // compile error
E e;
// F f; // compile error
}
Destructor
#include <iostream>
struct A
{
int i;
A ( int i ) : i ( i )
{
std::cout << "ctor a" << i << '\n';
}
~A()
{
std::cout << "dtor a" << i << '\n';
}
};
A a0(0);
int main()
{
A a1(1);
A* p;
{ // nested scope
A a2(2);
p = new A(3);
} // a2 out of scope
delete p; // calls the destructor of a3
}
Copy constructors
struct A
{
int n;
A(int n = 1) : n(n) { }
A(const A& a) : n(a.n) { } // user-defined copy ctor
};
struct B : A
{
// implicit default ctor B::B()
// implicit copy ctor B::B(const B&)
};
struct C : B
{
C() : B() { }
private:
C(const C&); // non-copyable, C++98 style
};
int main()
{
A a1(7);
A a2(a1); // calls the copy ctor
B b;
B b2 = b;
A a3 = b; // conversion to A& and copy ctor
volatile A va(10);
// A a4 = va; // compile error
C c;
// C c2 = c; // compile error
}
Copy assignment operator
#include <iostream>
#include <memory>
#include <string>
#include <algorithm>
struct A
{
int n;
std::string s1;
// user-defined copy assignment, copy-and-swap form
A& operator=(A other)
{
std::cout << "copy assignment of A\n";
std::swap(n, other.n);
std::swap(s1, other.s1);
return *this;
}
};
struct B : A
{
std::string s2;
// implicitly-defined copy assignment
};
struct C
{
std::unique_ptr<int[]> data;
std::size_t size;
// non-copy-and-swap assignment
C& operator=(const C& other)
{
// check for self-assignment
if(&other == this)
return *this;
// reuse storage when possible
if(size != other.size)
{
data.reset(new int[other.size]);
size = other.size;
}
std::copy(&other.data[0], &other.data[0] + size, &data[0]);
return *this;
}
// note: copy-and-swap would always cause a reallocation
};
int main()
{
A a1, a2;
std::cout << "a1 = a2 calls ";
a1 = a2; // user-defined copy assignment
B b1, b2;
b2.s1 = "foo";
b2.s2 = "bar";
std::cout << "b1 = b2 calls ";
b1 = b2; // implicitly-defined copy assignment
std::cout << "b1.s1 = " << b1.s1 << " b1.s2 = " << b1.s2 << '\n';
}
Move constructors
#include <string>
#include <iostream>
#include <iomanip>
#include <utility>
struct A
{
std::string s;
int k;
A() : s("test"), k(-1) { }
A(const A& o) : s(o.s), k(o.k) { std::cout << "move failed!\n"; }
A(A&& o) noexcept :
s(std::move(o.s)), // explicit move of a member of class type
k(std::exchange(o.k, 0)) // explicit move of a member of non-class type
{ }
};
A f(A a)
{
return a;
}
struct B : A
{
std::string s2;
int n;
// implicit move constructor B::(B&&)
// calls A's move constructor
// calls s2's move constructor
// and makes a bitwise copy of n
};
struct C : B
{
~C() { } // destructor prevents implicit move constructor C::(C&&)
};
struct D : B
{
D() { }
~D() { } // destructor would prevent implicit move constructor D::(D&&)
D(D&&) = default; // forces a move constructor anyway
};
int main()
{
std::cout << "Trying to move A\n";
A a1 = f(A()); // return by value move-constructs the target from the function parameter
std::cout << "Before move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n';
A a2 = std::move(a1); // move-constructs from xvalue
std::cout << "After move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n';
std::cout << "Trying to move B\n";
B b1;
std::cout << "Before move, b1.s = " << std::quoted(b1.s) << "\n";
B b2 = std::move(b1); // calls implicit move constructor
std::cout << "After move, b1.s = " << std::quoted(b1.s) << "\n";
std::cout << "Trying to move C\n";
C c1;
C c2 = std::move(c1); // calls copy constructor
std::cout << "Trying to move D\n";
D d1;
D d2 = std::move(d1);
}
Move assignment operator
#include <string>
#include <iostream>
#include <utility>
struct A
{
std::string s;
A() : s("test") { }
A(const A& o) : s(o.s) { std::cout << "move failed!\n"; }
A(A&& o) : s(std::move(o.s)) { }
A& operator=(const A& other)
{
s = other.s;
std::cout << "copy assigned\n";
return *this;
}
A& operator=(A&& other)
{
s = std::move(other.s);
std::cout << "move assigned\n";
return *this;
}
};
A f(A a) { return a; }
struct B : A
{
std::string s2;
int n;
// implicit move assignment operator B& B::operator=(B&&)
// calls A's move assignment operator
// calls s2's move assignment operator
// and makes a bitwise copy of n
};
struct C : B
{
~C() { } // destructor prevents implicit move assignment
};
struct D : B
{
D() { }
~D() { } // destructor would prevent implicit move assignment
D& operator=(D&&) = default; // force a move assignment anyway
};
int main()
{
A a1, a2;
std::cout << "Trying to move-assign A from rvalue temporary\n";
a1 = f(A()); // move-assignment from rvalue temporary
std::cout << "Trying to move-assign A from xvalue\n";
a2 = std::move(a1); // move-assignment from xvalue
std::cout << "Trying to move-assign B\n";
B b1, b2;
std::cout << "Before move, b1.s = \"" << b1.s << "\"\n";
b2 = std::move(b1); // calls implicit move assignment
std::cout << "After move, b1.s = \"" << b1.s << "\"\n";
std::cout << "Trying to move-assign C\n";
C c1, c2;
c2 = std::move(c1); // calls the copy assignment operator
std::cout << "Trying to move-assign D\n";
D d1, d2;
d2 = std::move(d1);
}
Converting constructor
struct A
{
A() { } // converting constructor (since C++11)
A(int) { } // converting constructor
A(int, int) { } // converting constructor (since C++11)
};
struct B
{
explicit B() { }
explicit B(int) { }
explicit B(int, int) { }
};
int main()
{
A a1 = 1; // OK: copy-initialization selects A::A(int)
A a2(2); // OK: direct-initialization selects A::A(int)
A a3{4, 5}; // OK: direct-list-initialization selects A::A(int, int)
A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
A a5 = (A)1; // OK: explicit cast performs static_cast, direct-initialization
// B b1 = 1; // error: copy-initialization does not consider B::B(int)
B b2(2); // OK: direct-initialization selects B::B(int)
B b3{4, 5}; // OK: direct-list-initialization selects B::B(int, int)
// B b4 = {4, 5}; // error: copy-list-initialization selected an explicit constructor
// B::B(int, int)
B b5 = (B)1; // OK: explicit cast performs static_cast, direct-initialization
B b6; // OK, default-initialization
B b7{}; // OK, direct-list-initialization
// B b8 = {}; // error: copy-list-initialization selected an explicit constructor
// B::B()
}
lvalue and rvalue functions
class A{
public:
void fun()&{
cout<<"can called only by lvalue";
}
void fun()&&{
cout<<"can called only by rvalue";
}
};