Endsieg77's Studio.

Effective C++

2021/01/03 Share

Introduction


View C++ as a federation of Languages

​ In Scott Meyer’s opinions, Modern C++ supports different paradigms of programming, including C, Object-Oriented C++, Template C++(with its byproduct of C++ Template Meta Programming, short for CTMP) and STL.


The Enum Hack

​ e.g.

1
2
3
4
5
6
7
8
class GamePlayer {
private:
enum { NumTurns = 5 }; // To make "NumTurns"
// a mark name of 5.
int scores[NumTurns]; // That would be alright.

...
};

Enum Hack可以替代宏定义常量,实现几乎相近的行为。没有名字的enum在编译时直接被编译器替换,不占用额外的内存。可以说是非常经济实惠的做法。(Enum Hack在C中也可以运用)


Overload the function with different constness

We always neglect the fact that two member functions with different constness can be overloaded.

e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// a template class of Array:
template<class _Ty, size_t _Size>
class
Array
{
...
typedef _Ty& reference;
typedef const _Ty& const_reference;
public:

...
const_reference operator[](const size_t&) const noexcept;
reference operator[](const size_t&) noexcept;
private:
...
};

// const version of operator[]:
template<class _Ty, size_t _Size>
inline
typename Array<_Ty, _Size>::const_reference
Array<_Ty, _Size>::operator[](const size_t &__index) const noexcept
{ return *(begin() + __index); }

// non-const version of operator[]:
template<class _Ty, size_t _Size>
inline
typename Array<_Ty, _Size>::reference
Array<_Ty, _Size>::operator[](const size_t &__index) noexcept
{
return const_cast<_Ty&>( // cast away the constness;
static_cast<const Array&>(*this) // cast *this into a const
[__index] // reference to call the
); // const version of operator[];
}

Use mutable to change member variables at will.


Assign the similar initialization tasks to member functions declared private
or delegation constructors to avoid the mechanical repetition of codes.


Realize simple singleton by:

1
2
3
4
Obj &SingletonFact() {
static Obj object {params};
return object; // return the handle of object
}

Constructor, Destructor, Assignment


Forbid the unwilled operations

​ Add an = delete at the end of the function to forbid the call of them.(After C++11)


EBO(To Be Continued)


Don’t declare function as virtual if unnecessary

​ Object must contain some information to judge which version of function to be called,

​ This information is usually pointed out by a pointer called vptr(virtural table pointer) which points an array made up of function pointers, called vtbl(virtual table), which causes the expansion of the size of objects.


Pure Virtual Function makes the class abstract

1
2
3
4
5
6
class Sample {
...
virtual ~Sample() = 0; // Sample cannot be instantiated now.
}

Sample::~Sample() { };

Never call a virtual function when the object has not been initialized

​ We can memorize it by a recipe: “Virtual function is not a virtual function, when the object is under construction.” Basically, the call of virtual function in this section violates the principles of runtime polymorphism.


Use Identity Test to avoid the self-assignment

1
2
3
4
5
6
7
Widget &Widget::operator=(const Widget &rhs) {
if(this == &rhs) return *this; // Identity Test.

delete pb; // pb is a member of Widget which points a Bitmap;
pb = new Bitmap(*rhs.pb);
return *this;
}

copy-and-swap strategy may function well

1
2
3
4
5
Widget &Widget::operator=(const Widget &rhs) {
Widget temp(rhs);
swap(temp);
return *this;
}

Resource Management

Smart Pointer

下面是一个工厂函数:

1
Investment *createInvestment();

我们希望构造的Investment在超出生命周期后立马被析构。那么我们可以用只能指针来封装Investment *

Smart pointer uses reference count as its gist to judge if the pointer packaged shall expire.

std::unique_ptr can be referred to only once.

std::shared_ptr can be referred to several times.

Circular Reference

std::shared_ptr should be combined with std::weak_ptr to avoid circular reference.

A Sample of Circular Reference:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A
{
public:
~A() {cout<<"A Expired"<<endl;}
shared_ptr<B> b;
};

class B
{
public:
~B() {cout<<"B Expired"<<endl;};
shared_ptr<A> a;
};

void Test()
{
shared_ptr<A> pa(new A);
shared_ptr<B> pb(new B);
pa->b = pb;
pb->a = pa;
}

调用Test()后发现A, B的析构函数均未被正常调用。

这个过程如下:

pa想要被析构,但pa的引用仍然被pb所指对象所把持,导致pa无法被析构。而pb也是如此,无法被正常析构。

我们只要把其中一个shared_ptr替换成weak_ptr即可。weak_ptr不具备资源管理的能力,其只提供了一个expired()接口检测资源是否被释放。


A Class Intended for Resource Management

Two Critical Criterion:

  • RAII, 获得资源后立刻放入资源管理对象。
  • 管理对象运用析构函数确保资源被释放。

Hana_Noa

CATALOG
  1. 1. Introduction
    1. 1.1. View C++ as a federation of Languages
    2. 1.2. The Enum Hack
    3. 1.3. Overload the function with different constness
    4. 1.4. Realize simple singleton by:
  2. 2. Constructor, Destructor, Assignment
    1. 2.1. Forbid the unwilled operations
    2. 2.2. EBO(To Be Continued)
    3. 2.3. Don’t declare function as virtual if unnecessary
    4. 2.4. Pure Virtual Function makes the class abstract
    5. 2.5. Never call a virtual function when the object has not been initialized
    6. 2.6. Use Identity Test to avoid the self-assignment
    7. 2.7. copy-and-swap strategy may function well
  3. 3. Resource Management
    1. 3.1. Smart Pointer
      1. 3.1.1. Circular Reference
    2. 3.2. A Class Intended for Resource Management