以下内容是c++中,大多是c没有的或与c不同的语言特性。
bool类型
值取true和false.
const 常量标识符
//const int a; // error,常量必须初始化 const int a = 100; // a = 200; // error,不能对常量赋值
const int * p // 定义了一个指向常量的指针变量(*p是常量)
int * const p2 = &a; // p2是常量,需初始化
/* const常量与#define定义的符号常量的区别
* 1.const定义的常量有类型,而#define定义的没有类型,编译可以对前者进行安全检查,对后者只是 * 做简单替换
* 2.const常量在编译时分配内存,#define定义的在预编译进行替换,不分配内存
* 3.作用范围不同,前者作用域为变量的作用域,后者从定义点到程序结束(也可用#undef取消)
*/
// 尽量用const、enum代替#define
内存对齐
- 是什么:1.编译器为每个“数据单元”安排在某个合适的位置上. 2.c,c++允许干涉内存对齐
- 为什么:在对齐的内存空间上能更快的访问数据
- 怎么用:
域运算符::
// 用法1:对与局部变量同名的全局变量进行访问 #include <iostream> using namespace std; int var = 100; int main() { int var = 50; cout << var <<endl; // 50 cout << ::var << endl; // 100 }
new与delete
- 是操作符,不是函数
- new用于创建堆空间,成功返回首地址, 用法:
- delete 释放堆空间
// new:分配内存,调用构造函数 int *p; p = new int; char *p_str = new char[50]; // delete:调用析构函数,释放内存 delete [] p_str; delete p;
重载
函数名相同,参数不同(数量,类型,顺序)
引用
- 就是给一个变量起别名(引用不是变量,只是变量的别名)
int a = 100; int &b = a; // b是a的别名
b = 200;
cout << b <<" " << a << endl; // 200 200 - 引用没有自己的空间
- 对引用的改变相当于对原变量的改变
- 用法:
// 1.作为函数参数 void swap(int &a, int &b) { int tmp = a; a = b; b = tmp; }
// 2.引用作为返回值,主要目的是可以将函数放在赋值运算符的左边(不能返回对局部变量的引用)
#include <iosteam>
using namespace std;
int a[] = {0, 1, 2, 3, 4};
int& index(int i)
{
return a[i];
}
int main()
{
index(3) = 100; // 引用作为函数返回值,使得函数可以放在赋值运算符左边
// 函数返回引用,引用在函数返回的时候初始化
cout << a[3] << endl; // 100
return 0;
}
引用与指针的区别
- 引用访问变量是直接访问,指针是间接访问
- 引用没有自己的内存空间,指针有
- 引用初始化后不能再引用其他变量,指针可以
(c++中尽可能使用引用)
内联函数
1.背景
首先来看函数调用的过程:
建立占空间->保护现场->传递参数->控制程序执行的转移->...
对于一些本身功能简单,代码简短,使用频率很高的函数,频繁调用会严重影响程序效率,可以通过将代码嵌入程序中,但这样又会产生大量重,为了解决效率与重复的矛盾,c++提供了一种方法——内联函数(定义函数时使用 inline 修饰).
内联函数是在编译的时候展开的(相当于嵌入到代码中,不存在函数调用).
2.内联函数与带参数宏的区别
- 内联函数要求实参形参类型一致,先对实参表达式求值,然后传递给形参;而宏调用时只用形参进行简单的替换.
- 内联函数在编译时使用、在调用的地方将代码展开;宏调用在预编译时进行替换
新的类型转换
旧式:
(T)expr
新式:
- const_cast<T>(expr)
// 移除对象的常量性 // 一般用于指针或者引用 // 去除const限定不是为了修改它的内容 // 通常是为了函数能接受这个参数 #include <iostream>
void fun(int &val)
{
cout << "test" << endl;
}
using namespace std; int main() { const int val = 100; // int n = const_cast<int> (val); // error,不能用于对象 int n = val; // int *p = &val; error,无法从const int * 转换为int * int *p = const_cast<int*>(&val); *p = 200; cout << val << endl; // 100,并未改变,产生临时对象
const int val2 = 200;
// int& ref2 = val2; 非const引用不能指向const对象
int& ref2 = const_cast<int&>(val2);
ref2 = 300;
cout << val2 << " " << ref2 << endl; // 200 300
// func 函数要求传入非const引用,这时可以用const_cast对val2进行转换
fun(const_cast<int&>(val2)) return 0; } - static_cast<T>(expr)
- 编译器的隐式转换:编译器自动转换,一般来说是安全的,比如说int a; short b; a = b; 编译器隐式执行的任何类型转换都可以有static_cast完成;
- 当一个较大的算术类型赋值给较小的类型时,可以使用static_cast进行强制转换,例:
// double -> int
#include <iostream> using namespace std; int main() { int n = static_cast<int>(3.14); cout << n << endl; // 3 return 0; } -
可以将void*指针转换为某一类型的指针;
- 可以将基类指针转成派生类指针
- 无法将const转换为nonconst
- reinterpret_cast<T>(expr)
/* * reinterpret_cast:将数据以二进制存在形式的重新解释 */ int i; char *p = "This is a example."; i = reinterpret_cast<int>(p); // 此时结果,i与p完全相同 int *ip; char *pc = reinterpret_cast<char *>(ip); // 需要记得pc所指向的真实对象是int型,并非字符串。 // 如果将pc当作字符指针进行操作,可能会造成运行时错误 // 如int len = strlen(pc);
- dynamic_cast<T>(expr): 只接受基于类对象的指针或引用的类转换
尽可能避免转换,若无法避免使用新式类型转换