定义简单类型变量
int a = 5; // C & CPP
int a(5); // CPP
char * str = “12345”; // c & cpp
char * str(“china”); // cpp
// CPP宽字符
wchar * str(L”呵呵”);
wcout << str; // 宽字符的输出 wcout,而不是cout
宽字符和窄字符的区别:
- char属于C和C++的最常见的基本数据类型,长度为一个字节,只能存储ASII码。
- 但是下面这行代码不会报错但是c的值却不是汉字‘汉’,因为只有一个字节,所以无法存储,打印出来是乱码。char c = ‘汉’;
建议采用CPP方式,括号初始化。
命名空间namespace
namespace作用:解决类重名、函数重名、变量重名等重名问题。
namespace内容的访问类型:
- namespace中所有的内容都是public的,且不能用private修饰。
- 但是cpp中struct可用public、private修饰,且结构体默认数据是public的。
- class中默认内容是私有private的
C没有namespace关键字,cpp才有命名空间概念,这也是在cpp中头文件无后缀.h的原因(区分是c的还是cpp的头文件(cpp版本头文件中可用namesapce))
iostream是C++的头文件,iostream.h是C的头文件,即标准的C++头文件没有.h扩展名,将以前的C的头文件转化为C++的头文件后,有时加上c的前缀表示来自于c,例如cmath就是由math.h变来的。 iostream.h里面定义的所有类以及对象都是在全局空间里,所以你可以直接用cout 但在iostream里面,它所定义的东西都在名字空间std里面,所以你必须加上 using namespace std才能使用cout
命名空间的定义:namespace SPACE_NAME { body…}
命名空间的引用
- a) using namespace SPACE_NAME;
- b) SPACE_NAME::成员NAME
匿名namespace:匿名namespace,表示可直接引用该space内的内容,而不用SPACE_NAME::的方式调用该域内容
命名空间别名
namespace name1_long_name{} namespace name2 = name1_long_name;//变量赋值的方式为命名空间取别名
命名空间嵌套:命名空间可以嵌套
扩充命名空间内容
- 再次重定义命名空间(空间名相同)则会扩展原命名空间的内容,而不像变量那样因重定义而编译出错。
- 但是,在扩展的时候,不能再次重定义变量,不会覆盖而是发生重定义编译错误。
- 不建议在命名空间中直接定义函数,而是采用函数指针变量的方式,在命名空间外部定义函数
using作用域
- using作用只从该语句到本源文件结束。
- 若using语句在block内部,则该using只作用在该代码块中
- using语句只能在命名空间定义之后的地方使用
- 一个源文件可用同时使用多个using语句,但是若同时using的多个命名空间有名称相同的内容,则引用该内容时需要显式的采用域操作符::方式。
C中全局变量与CPP命名空间
- C中,当函数中局部变量与全局变量重名时,在该函数中无法引用该全局变量
- CPP中,可用::操作符解决上述情景,可在函数中引用重名的全局变量,::name表示name是全局的变量,而不是该代码块中的局部变量
- 综上所述,cpp中的::域操作符,若之前有标识符,表示命名空间,无标识符,表示全局变量
函数重载
- 重载:参数类型,参数个数,参数顺序,函数名相同
- C没有函数重载,但是cpp有函数重载,故cpp兼容c时,需要用到extern关键字。
- 函数的默认参数:C不可以,cpp可以使用默认参数,但是默认参数只能在参数列表的最后。
含有默认参数的重载函数,在调用的时候会发生调用错误,编译器不知道到底该调用哪个函数,此时,可用函数指针解决该问题,或者用namespace解决。
void test(int a,int b=1) { cout<< a << b; } void test(int a) { cout << a; } int main(int argc, char const *argv[]) { // 调用test的时候,若test(2);则不知道如何匹配 // 此时用namespace或者函数指针解决 void (*fun1)(int a,int b) = test; // 用指针解决的时候,默认参数不再起作用,要显式传入参数值 fun1(2,1); return 0; }
auto
- C与cpp中auto都是自动变量(自动匹配类型),但有所不同
- C中无法获取类型。
- cpp中,可以使用auto循环遍历数组,语法类似于java的foreach,必须是一个数组的常量指针
附:cout打印的时候会默认屏蔽小数点后无意义的0,如1.000打印成1.
CPP类型转换,C方法括号,CPP方法static_cast
左值与右值,引用(难点)
- 最大的区别:是否可取地址,是否可赋值。左值必须在内存中有实体,而通常右值暂存在cpu的寄存器中
- 左值引用:
int a = 5;int &ra(a);
- 指针的引用:
int * pa(&a);int * &rpa = pa;//rpa是pa的副本,别名,引用,类型是int*的
- 引用右值:如,取地址是右值,CPU寄存器中,右值引用常用于操作底层cpu。例见代码
- 函数返回引用:返回的是栈上局部临时变量的引用值。区分引用的是栈中内容还是堆中内容。
// 左右值引用的语法和区别
// int 变量,初始为5
int a(5);
// int类型的指针,指向a
int * pa(&a);
// int指针 类型的左值引用,引用的是指针变量pa
int * & rpa1(pa);
// int指针 类型的右值引用,引用的是int类型数据a的地址
//(取地址在CPU中完成,结果保存在cpu的寄存器中,即&a这个值是在寄存器,&a这个值对应的地址是在内存中的)
int* && rpa2 = &a;
/* 堆栈的引用,试验结果与笔记有所出入,vs下qt下结果略有不同 */
// 返回值为:栈中内容的引用
int & test1()
{
// a 是栈中局部变量,函数调用结束回收内存
int a = 10;
int &ra = a;
return ra;
}
// 返回值为:堆中内容的引用
int * & test2()
{
// 通过new申请的堆内存,不会随函数调用结束而被回收重利用
int * a = new int(100);
cout << "a指向的堆地址:" << a <<endl;
cout << "指针变量a自身的地址(栈变量地址):" << &a <<endl;
int * &ra = a;
return ra; // 相当于返回的是指针变量a(的 别名\副本 变量)
}
int main(int argc, char *argv[])
{
// int类型引用接收函数返回值
int & ra = test1();
// 输出结果a=10;
/* 原因:ra是左值引用,实际是通过指针实现,与被引用变量操作同一块内存
* 而现在test1中的a是栈中局部变量,函数调用后,不再占用该内存,也就是说
* ra指向的是一块它不再持续占用的内存,但此时,这个栈内存还没有被其他操作
* 占用,故其中的内容没变,故访问其内容,输出的还是10 */
cout << "a = " << ra <<endl;
// 输出结果a=574548;
/* 原因:cout实际是操作符重载函数的调用,在函数调用过程中,占用了栈内存。
* 故tset1中的a的内存,也就是ra的实际内存,被操作了,修改成了任意函数调用
* 中间值,故此时再访问,输出就不是10了,而是任意值 */
cout << "a = " << ra << endl;
cout << endl;
// -----------------------------------------------
// 这是一个int指针的副本,这个int指针变量是栈中局部变量,随时会被清除重利用
int * & pra = test2();
// 故先将该指针所指向的堆内存地址保存下来
int * p = pra;
cout << *pra << endl;
cout << *p << endl;
cout << *pra << endl;
cout << *p << endl;
delete(p);
cout << *pra << endl;
cout << *p << endl;
return 0;
}
引用是CPP内容,C不支持,引用必须初始化, = 或者 () 两种初始化方法
引用变量和被引用变量操作的是同一块内存,但指针和引用不完全相同。
malloc和free,new 和delete区别,C中free的指针还能访问,但CPP中为了安全,delete之后的指针,实际上是指向了一块安全的内存区域,该区域禁止访问,访问就会程序中断。
const
- CPP中可用const int常量值来初始化数组长度,而c不能通过编译。
const int num=4;int a[num];
。 - CPP中,一旦const,则无法修改。C中可以通过指针修改。但CPP中可通过const_cast方式强制去除const属性(但仍然无法修改),此处坑点:不同编译器会做不同处理,实际上常量内存已通过指针修改,但是有的编译器在读取常量的时候直接从常量表中读取替换,而不从内存读取,感觉就是指针没有修改const数据,比如vc的编译器就是这样的
- 几种const写法的差别:常指针与指向常量的变指针
int a = 10;
const int b = 5;
// 此处 const int == int const
// 在*左边,表示p1指向的是int常量,指向的内存中数据不能修改
// 但p1本身是个指针变量,p1可修改,即可指向其他地址
const int * p1 = &a;
int const * p2 = &b;
p1 = &b; // 合法,p1可改变指向
p2 = &a; // 合法,同上
*p1 = 3; // 不合法,p1指向的内存是const int,常量,不能修改
*p2 = 6; // 不合法,同上
int * const p3(&a); // 合法,p3是常指针,不能修改指向,但指向的内存可修改存储数据
int * const p4(&b); // 不合法,p3指向的内存规定是int型,但&b是const int 型,不匹配
p3 = &b; // 不合法,p3是常指针,无法修改指向
p4 = &a; // 不存在
*p3 = 4; // 合法,p3指向的内存可修改,但需要改成int型
*p4 = 8; // 不存在
const int * const p5(&a);// 指向常量的常量指针
//注:此处a是变量而不是常量,但操作仍成功,因为此处p5只读,不能修改a的值,a的类型是否const无意义,编译器无视
- 常引用:拷贝构造函数参数就是常引用,保证不会修改被引用的数据
通常常量标识符,用大写
new delete
- int num; int *p = new int;前者栈中,后者堆中
- VC中,delete p,再访问*p,会无法访问产生中断。CPP中无法重复delete一个指针两次
- delete数组,基本数据类型,可直接delete,复杂数据类型,需要 delete []p
- 重载new和delete时,若需要调用默认的new和delete,使用全局域操作符::new和::delete