C++惯用法
RAII惯用法
RAII:(Resoure Acquisition Is Initialization,资源获取即初始化)
- 在构造函数调用资源获取
- 对象销毁即释放资源(对象离开作用域,自动清理和释放)
- 把资源和对象的生命周期绑定,对象创建获取资源,对象销毁释放资源
- 避免程序员犯错误,忘记销毁相关资源,造成悬空指针
- 出现异常时,最后写的手动销毁函数不一定会被调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
template<T>
class RAII
{
public:
RAII()
{
mptr=new T();
};
~RAII()
{
delete mptr;
}
private:
T* mptr;
}
|
pimpl惯用法
- 将头文件与库提供给第三方使用时,将关键成员变量和私有函数实现隐藏,将其包装到一个辅助类——Impl。原类有一个指向Impl的指针来调用。从而在头文件隐藏具体参数
构造函数
构造函数:无参数(初始化表达式)
一般的构造函数在{}内赋值
1
2
3
4
5
6
7
8
9
10
|
class Construct
{
int m_num;
std::string m_name;
Construct()
{
m_num(1);
m_name("Zhang3")
}
}
|
这样会现在{}外初始化成员参数后,在{}内再赋值一次
比初始化表达式多赋值一次。 会造成复制的性能浪费。
- 且若成员变量是 const ,则只能在初始化表达式中初始化,不能在{}中赋值。
初始化表达式初始化:
1
2
3
4
5
6
|
class Construct
{
int m_num;
std::string m_name;
Construct():m_num(1),m_name("Zhang3"){}
};
|
注意:只能用在无参数构造函数,或不需要赋值的成员变量使用,否则在{}中仍然要 二次赋值会造成性能损失。
构造函数:单个参数(陷阱)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Pig
{
int m_weight;
std::string m_name;
Pig(int weight):m_name("I am a "+std::tostring(weight)+"kg pig"){}
};
void showMe(Pig pig)
{
std::cout<<"name:"<<pig.m_name<<std::endl;
}
int main()
{
show(80); //第一种方法,隐式转换
show(Pig(80)); //第二种方法,直接赋值
}
|
可以看到,第一种方法会把 80隐式调用单参数构造函数,隐式转换成 Pig类型,再赋值。
这样会造成许多误会,会让类型与int类似,无法分辨。
甚至还可以进行这样的赋值初始化:
1
|
Pig pig=100 //隐式调用 Pig(int weight)构造函数
|
为了避免隐式的调用构造函数,隐式转换
可以使用关键字 explicit.
explicit 关键字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Pig
{
int m_weight;
std::string m_name;
explicit Pig(int weight):m_name("I am a "+std::tostring(weight)+"kg pig"){}
};
void showMe(Pig pig)
{
std::cout<<"name:"<<pig.m_name<<std::endl;
}
int main()
{
show(80); //第一种方法,隐式转换 构造函数有explicit 编译错误
show(Pig(80)); //第二种方法,直接赋值 编译成功
}
|
添加了explicit关键词后,只能显式赋值。
构造函数:多参数
同样是Pig类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class Pig
{
int m_weight;
std::string m_name;
Pig(int weight,std::string name):m_name(name),m_weight(weight){}
};
void showMe(Pig pig)
{
std::cout<<"name:"<<pig.m_name<<std::endl;
}
int main()
{
Pig pig={80,"佩奇"}; //{}初始化列表赋值
show({120,"肉猪"}); //{}初始化列表构造临时对象
}
|
参考: