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 *p
和char 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对象最好就不要出现