Featured image of post Effective C++ 01 - 开始习惯这门语言

Effective C++ 01 - 开始习惯这门语言

不能只学习,还要用起来

让自己习惯 C++

# 视 C++为一个语言联邦

现如今的 C++是个多重泛型编程语言,同时支持过程形式,面向对象形式,函数形式,泛型形式,元编程形式;主要成分如下:

  • C: blocks, statements, preprocessor, built-in data types, arrays, pointers
  • Object-oriented C++: classes, encapsulation, inheritance, polymorphism, virtual
  • Template C++
  • STL: containers, iterator, algorithms, function objects

# 尽量以 const, enum, inline 替换 #define

#define不被视为语言的一部分。

  • 对于单纯变量,最好以 const 对象或 enums 对象替换#define
  • 对于形式函数的宏,最好改用 inline 函数替换#define

宏相关的内容,我大都不太了解。。。

# 尽可能使用 const

**const 允许指定一个语义约束,然后编译器会强制实施这项约束。**可以使用 const 在 classes 外部修饰 global 或 namespace 作用域中的常量,或修饰文件、函数、区块作用域中被声明为 static 的对象。

1
2
3
4
5
6
// 如果出现在*左边,表示被指物是常量;如果出现在*右边,表示指针是常量。
char greeting[] = "Hello";
char* p = greeting;
const char* p = greeting;
char* const p = greeting;
const char* const p = greeting;

const 成员函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class TextBlock {
public:
    const char& operator[](std::size_t pos) const { return text[pos]; }
    char&       operator[](std::size_t pos) { return text[pos]; }

private:
    std::string text;
};
TextBlock tb("Hello");
std::cout << tb[0]; // call non-const

const TextBlock ctb("World");
std::cout << ctb[0]; // call const

# 确定对象被使用前已被初始化

读取未初始化的值会导致不明确的行为,而这也是我们写 C++的时候极力想避免的。

1
2
// 不同编译环境下,x不一定保证被初始化(0)
int x;

最佳的处理办法是:永远在使用对象之前将它初始化。

1
2
3
4
5
// 内置类型的初始化
int x = 0;
const char* text = "A C-style string";
double d;
std::cin >> d;

对于非内置类型以外,初始化的责任由构造函数负责,也就是说需要确保每一个构造函数都将对象的每一个成员初始化。

C++规定对象的成员变量的初始化动作发生在进入构造函数本体之前。

 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
#include <list>
#include <string>

class PhoneNumber {};

class AddressEntry {
public:
    AddressEntry(const std::string& name, const std::string& address,
                 const std::list<PhoneNumber>& phones);

private:
    std::string            name;
    std::string            address;
    std::list<PhoneNumber> phones;
    int                    numTimesConsulted;
};

// assignment
AddressEntry::AddressEntry(const std::string& name, const std::string& address,
                           const std::list<PhoneNumber>& phones) {
    this->name              = name;
    this->address           = address;
    this->phones            = phones;
    this->numTimesConsulted = 0;
}

// initialization (member initialization list) 效率更高
AddressEntry::AddressEntry(const std::string& name, const std::string& address,
                           const std::list<PhoneNumber>& phones)
    : name(name), address(address), phones(phones), numTimesConsulted(0) {}

non-local static 对象: 其寿命从被构造出来知道程序结束为止,不属于 stack 和 head-based 对象。包括 global 对象,定义于 namespace 作用域内的对象,在 classes 内,在函数内,在 file 作用域内被声明为 static 的对象。

C++对"定义于不同编译单元内的 non-local static 对象"的初始化次序并无明确定义。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 文件系统API定义
class FileSystem
{
public:
    std::size_t numDisks() const;
};

extern FileSystem tfs; // 给外界使用的对象

// 用户使用
class Directory
{
public:
    Directory();
};
// 两个源文件中的对象,不确定哪一个先行初始化
Directory::Directory()
{
    std::size_t disks = tfs.numDisks(); // 使用tfs对象
}

使用单例模式,解决上述问题。

 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
// 文件系统API定义
class FileSystemNew
{
public:
    FileSystemNew &ntfs()
    {
        static FileSystemNew fs;
        return fs;
    }
    std::size_t numDisks() const;
};

// 用户使用
class DirectoryNew
{
public:
    DirectoryNew();
};
// 两个源文件中的对象,不确定哪一个先行初始化
DirectoryNew::DirectoryNew()
{
    std::size_t disks = ntfs().numDisks(); // 使用tfs对象
}
DirectoryNew &tempDir()
{
    static DirectoryNew dir;
    return dir;
}
The older I get, the more I realize that most of life is a matter of what we pay attention to, of what we attend to [with focus].
Built with Hugo
Theme Stack designed by Jimmy