Effective C++中文版学习记录(一)

Effective C++中文版学习记录(一)

章节一:让自己习惯C++

进度:4/55



条款01、视C++为一个语言联邦

C++三特性:封装、继承、多态

同时C++具有模板化的思路、并且具有STL库可以使用

所以C++可以被称为一种语言的联邦,由C、Object-Oriented C++、Template C++、 STL四个次语言组成

条款02、尽量以const,enum,inline替换#define

在使用define时,如:

#define ASPECTED 0.642

如果在运行该变量时代码报错,报错信息会直接输出0.642相关,而不是ASPECTED,这会导致代码维护性降低

所以解法是:

const double ASPECTED = 0.642;

而enum在这里引入是为了介绍一个方法:enum hack

考虑这样一个类,类内定义了一个变量,而另一个变量需要以该变量为基础来构建

class Game {
private:
    static const int GameTurn = 10;
    int scores[GameTurn];
};

明显看出GameTurn在类内就初始化为10,但是有些C++并不能编译这个代码,所以要用enum来解决

class Game {
private:
    enum {GameTurn = 10};
    int scores[GameTurn];
};

这里enum相当于做了一个define,将GameTurn定义为10,但是#define并不能对类内的元素定义,所以需要enum来完成

而inline代替define的原因是考虑到这样的一个情况:

#define CALL_WITH_MAX(a, b) f( (a) > (b) ? (a) : (b) )

这样一个像函数的宏定义看着都很难受,而且受限,比如无法写成类内的private函数

所以引入inline的概念,定义inline函数为:

template<typename T>
inline void callWithMax(const T& a, const T& b)
{
	f(a > b ? a : b);
}

这样定义更为清晰,规范,而且真正的是一个函数,符合很多代码的需求

条款03、尽可能使用const

const的意义是说:这个变量不可以修改

常问的问题是:

char * p
const char * p
char * const p
const char * const p

这四种指针有什么区别,这个分析也简单

只需要看const修饰的是哪个东西就可以了

char *p                                 // 无const,所以data和pointer都可改变
const char * p                          // const修饰的是char,所以data不能变,但是pointer可变
char * const p                          // const修饰的是p,所以pointer不能变,但是data可以变
const char * const p                    // 第一个const修饰char,第二个修饰p,所以data和pointer都可以变

不过要注意,const char *pchar const * p是一样的,所以是需要*来判断const修饰谁

const *那就不是修饰指针,* const就是在修饰指针

所以const常常在函数的参数定义上作修饰,告诉函数,哪些参数是不允许修改的,能大大保证程序安全

同时也可以用于修饰函数的返回值,防止返回值被修改

OK,那么考虑这样一个情况,定义的一个类,有一个函数是operator[]

const char & operator[] ...
char & operator[] ...

这两个函数实现的功能是一样的,那为什么要写两个?因为定义的类是可以为const和non-const两种类型

那么对应的函数也需要对这两种进行匹配,不然non-const型的这个类是无法调用const型的operator[]的

所以这里有什么问题?两个函数实现功能一样,难道要复制一遍吗

当然可以,实现上完全没区别,但是更好的做法是令non-const调用const函数,防止代码重复,只需要对传入的参数进行转型

条款04、对象使用前应该被初始化

这个要牢牢记住,比如

int i;
vector<int> test(i, 0);

i没有初始化就用去创建vector,那后果是完全没法预测的,内置型对象一定要手工初始化,在类内也应当初始化再使用

在C++ 11中,可以直接在初始化时定义了

class MyClass {
public:
    int uninitializedInt = 0;      // 类内默认初始化
    double uninitializedDouble = 0.0; // 类内默认初始化
    std::string initializedString = "Hello"; // 类内默认初始化

    // 其他成员函数...
};

在class的使用中,需要介绍一下non-local static和local static,最好是只有后者,而不要有任何前者。举例:

// file1.cpp
#include <iostream>

class Logger {
public:
    Logger() { std::cout << "Logger Initialized\n"; }
    void log(const std::string& message) { std::cout << "Log: " << message << std::endl; }
};

Logger globalLogger; // non-local static对象

void logMessage(const std::string& message) {
    globalLogger.log(message);  // 会依赖globalLogger对象实现
}

// file2.cpp
#include "file1.cpp"

class Application {
public:
    Application() { logMessage("Application Initialized"); }
};

Application globalApp; // 另一个non-local static对象

这里globalApp的初始化会调用logMessage,而logMessage的运行依赖globalLogger

然而,globalLogger可能在此时并未初始化,那么logMessage就会报错,那就完了

解决方法是:

// file1.cpp
#include <iostream>

class Logger {
public:
    Logger() { std::cout << "Logger Initialized\n"; }
    void log(const std::string& message) { std::cout << "Log: " << message << std::endl; }
};

Logger& getLogger() {
    static Logger logger; // local static对象
    return logger;
}

void logMessage(const std::string& message) {
    getLogger().log(message);
}

// file2.cpp
#include "file1.cpp"

class Application {
public:
    Application() { logMessage("Application Initialized"); }
};

Application globalApp; // non-local static对象,但不再依赖于globalLogger

把globalLogger放在函数中,利用函数来初始化这个变量,这样就能保证它一定存在

但是注意,globalApp的存在其实还是不太合规,这种non-local static对象最好就不要出现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值