导语:如果,将编程语言比作武功秘籍,C++无异于《九阴真经》。《九阴真经》威力强大、博大精深,经中所载内功、轻功、拳、掌、腿、刀法、剑法、杖法、鞭法、指爪、点穴密技、疗伤法门、闭气神功、移魂大法等等,无所不包,C++亦如是。
C++跟《九阴真经》一样,如果使用不当,很容易落得跟周芷若、欧阳锋、梅超风等一样走火入魔。这篇文章总结了在学习C++过程中容易走火入魔的一些知识点。为了避免篇幅浪费,太常见的误区(如指针和数组、重载、覆盖、隐藏等)在本文没有列出,文中的知识点也没有前后依赖关系,各个知识点基本是互相独立,并没有做什么铺垫,开门见山直接进入正文。
目录
1 函数声明和对象定义
2 静态对象初始化顺序
3 类型转换
3.1 隐式转换
3.2 显示转换
4 inline内联
5 名称查找
5.1 受限名称查找
5.2 非受限名称查找
6 智能指针
6.1 std::auto_ptr
6.2 std::shared_ptr
6.3 std::unique_ptr
7 lambda表达式
对象定义写成空的初始化列表时,会被解析成一个函数声明。可以采用代码中的几种方法定义一个对象。
//这是一个函数声明
//不是一个对象定义
string foo();
//函数声明
string foo(void);
//对象定义几种方法
string foo;
string foo{ };//c++11
string *foo = new string;
string *foo = new string();
string *foo = new string{ };//c++11
(左滑可以查看全部代码,下同)
在同一个编译单元中,静态对象的初始化次序与其定义顺序保持一致。对于作用域为多个编译单元的静态对象,不能保证其初始化次序。如下代码中,在x.cpp和y.cpp分别定义了变量x和y,并且双方互相依赖。
//x.cpp
extern int y;
int x = y + 1;
x.cpp中使用变量y来初始化x
//y.cpp
extern int x;
int y = x + 1;
y.cpp中使变量x来初始化y
//main.cpp
extern int x;
extern int y;
int main()
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
return 0;
}
如果初始化顺序不一样,两次执行的结果输出不一样,如下所示:
g++ main.cpp x.cpp y.cpp
./a.out
x = 1
y = 2
g++ main.cpp y.cpp x.cpp
./a.out
x = 2
y = 1
如果我们需要指定依赖关系,比如y依赖x进行初始化,可以利用这样一个特性来实现:函数内部的静态对象在函数第一次调用时初始化,且只被初始化一次。使用该方法,访问静态对象的唯一途径就是调用该函数。改写后代码如下所示:
//x.h
extern int &getX();
//x.cpp
int &getX()
{
static int x;
return x;
}
getX()函数返回x对象
//y.h
extern int &getY();
//y.cpp
#include "x.h"
int &getY()
{
static int y = getX() + 1;
return y;
}
y对象使用x对象进行初始化
//main.cpp
int main()
{
cout << "x = " << getX() << endl;
cout << "y = " << getY() << endl;
return 0;
}
打印x和y值。通过这种方式,就保证了x和y的初始化顺序。
g++ main.cpp x.cpp y.cpp
./a.out
x = 0
y = 1
g++ main.cpp y.cpp x.cpp
./a.out
x = 0
y = 1
这里只描述自定义类的类型转换,不涉及如算数运算的类型自动提升等。
3.1 隐式转换
C++自定义类型在以下两种情况会发生隐式转换:
1) 类构造函数只有一个参数或除第一个参数外其他参数有默认值;
2) 类实现了operator type()函数;
class Integer
{
public:
Integer() : m_value(0) { }
//int --> Integer
Integer(int value) : m_value(value)
{
cout << "Integer(int)" << endl;
}
//Integer -->