从C到C++语法过度1
1. 字符串string
C语言从本质上来说,是没有字符串这种类型的,在C语言中如果要表达字符串,只能间接地借助于字符指针或者字符数组来表达,很明显这是由于C语言的诞生年代过于久远而导致的一种设计缺陷。
在C++中,字符串就跟整型、浮点型数据一样,是系统原生支持的一种基本数据类型:
string s;
有了字符串string这种类型之后,很多对字符串的操作就变得非常简单而直观了,C++对string类型的支持包括:
// 字符串的赋值
string s1 = "abdc";
// 字符串的复制
string s2;
s2 = s1;
// 字符串的拼接
string s3;
s3 = "xyz" + s1;
s3 += "123";
// 字符串的比对
if(s1 == "abcd")
cout << s1 << " == abcd" << endl;
else if(s1 > "abcd")
cout << s1 << " > abcd" << endl;
else if(s1 < "abcd")
cout << s1 << " < abcd" << endl;
// 字符串中的元素引用
s1[1] = 'B'; // 将s1修改为"aBcd"
当然,字符串 string
类型数据的操作接口远远不止以上那些,基本上我们能想到的任何对字符串的操作,这个类型本身都支持的。
2. 引用
概念:给一个已有对象取一个别名
语法:
int a = 100;
int &ra = a; // 从此之后,a 跟 ra 是同一个变量,代表同一块内存
// 以下代码具有等价的作用
a = 200;
ra = 200;
注意点:
- 引用必须在定义的同时赋值,不可单独定义引用,例如 int &r; 是错误的。
- 引用一方面提高了数据传输的效率,另一方面简化了数据表达的样式(跟指针相比)
3. 类型转换
对于数据类型转换,C++完全兼容C语言的类型转换语法,同时又新加了更合理的书写形式和更安全的新式类型转换,如下面所示:
旧式转换
(char)a; // C语言风格类型转换
char(a); // C++风格类型转换
新式转换
const_cast<char *>(a);
static_cast<char *>(a);
dynamic_cast<char *>(a);
新式转换是C++的全新特性,这几个转换关键字含义如下:
const_cast
: 专用于去除指针或引用的const
属性static_cast
: 与旧式转换相近,但提供了更易于查找的语法,并能有效识别不兼容类型。dynamic_cast
: 专用于类类型的上下代际间的转换
3.1 新式转换 const_cast
首先,先要明确 const_cast
的使用规范:
const_cast
旨在去除标识符的cv限定属性(即const
与volatile
)const_cast
只能作用于指针或引用类型
以下是 const_cast
的具体用法:
处理 const 型引用
int main()
{
int i = 6; // 普通整型变量
const int &ri = i; // const型引用,不可修改
// ×: 以下语句错误
ri = 8;
// √: 去除 const 特性后,可以赋值
const_cast<int &>(ri) = 8;
}
处理常目标指针
int main()
{
int i = 6;
const int *p = &i; // 常目标指针p,不可修改目标
// ×: 试图修改常指针的目标,错误!
*p = 8;
// √: 去除 const 特性后,可以修改其目标
*(const_cast<int *>(p)) = 8;
// ×: 试图扩大权限,错误!
int *k = p;
// √: 去除 const 特性后,可赋值给普通指针 k
int *k = const_cast<int *>(p);
}
注意,虽然 const_cast
可以将常目标指针的 const
属性剔除掉,但它不能剔除普通变量和常指针本身的 const
属性。例如:
int main()
{
int i = 6; // 普通整型变量
int *const p = &i; // 常指针
int j = 8;
// ×: p是常指针,无法修改其指向
p = &j;
// ×: const_cast 不能去除常指针的 const 属性
const_cast<int *>(p) = &j;
}
上述代码以 const
属性为例,讨论变量的 volatile
属性时是完全一样的。
3.2 新式转换 static_cast
static
意味着静态转换,静态的含义是操作的过程只发生在编译阶段,而不是运行阶段,静态转换不涉及类型推理。
增加可读性
int main()
{
float f = 3.14;
// 旧式类型转换
int i = (int)f;
int i = int(f);
// 新式静态转换,等价于旧式转换:
// 但是,新式静态强转更具可读性,更容易被查找
int i = static_cast<int>(f);
}
提高安全性
对于旧式类型转换,奉行了C语言的一贯作风:基本不进行任何逻辑判定,将灵活性和责任都丢给开发者。这样做的后果是可能会在某些比较难以察觉的地方埋下隐患,使用 static_cast
可以避免某些隐患。
int main()
{
int i = 6; // 普通整型数据
float *pf; // 普通浮点指针
// 旧式转换不进行任何合理性检查
// 下面的代码,可以将类型问题瞒天过海
// 编译器让其畅行无阻,开发者肉眼也难以察觉
pf = (float *)&i; // float * 与 int * 不兼容,照样通过编译
// 使用 static_cast 遇到非兼容性类型转换,会提出警告甚至错误
pf = static_cast<float *>(&i); // float * 与 int * 不兼容
}
注意,虽然 static_cast
可以将不兼容的普通数据类型转换明确指出来,但对于代际类类型的上下层转换无能为力,对于类类型的代际上行转换或下行转换,需要借助 dynamic_cast
去保证运行的安全性,这个知识点要等到学完类之后再展开。
4. 关键字auto
在C语言中,auto
用来声明一个存储在栈的变量,因此它只能用来修饰临时变量,不能用来修饰全局变量、静态变量,与此同时临时变量本身默认就是存储在栈中,因此在C语言中,auto
基本上是作废的。
在C++中,auto
代表自动获得数据的类型,比如:
auto a = 100; // 等价于: int a = 100;
当然,上述代码仅是语法示例,无法体现 auto
的价值,在C++的函数模板、类模板中,利用 auto
自动获得数据类型,往往有奇效,例如:
// 以下函数是一个模板,接收一个类型为 T 的容器
// 注意:类型 T 是动态的,可变的,不定的。
template <typename T>
void show(T &container)
{
// 利用 auto 动态获取未知类型容器的相关数据
auto it = container.begin();
...
...
}