目录
1. C++的第一个程序
C++兼容C语⾔绝⼤多数的语法,所以C语⾔实现的hello world依旧可以运⾏,C++中需要把定义⽂件 代码后缀改为.cpp,vs编译器看到是.cpp就会调⽤C++编译器编译,linux下要⽤g++编译,不再是gcc
#include<stdio.h>
int main()
{
printf("Hello world!\n");
return 0;
}
#include<iostream>
using namespace std;
int main()
{
cout << "Hello world!" << endl;
return 0;
}
2. 命名空间
2.1 namespace的价值
在C/C++中,变量、函数和后⾯要学到的类都是⼤量存在的,这些变量、函数和类的名称将都存在于全 局作⽤域中,可能会导致很多冲突。使⽤命名空间的⽬的是对标识符的名称进⾏本地化,以避免命名 冲突或名字污染,namespace关键字的出现就是针对这种问题的。
2.2 namespace的定义
- 定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中 即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
- namespace本质是定义出⼀个域,这个域跟全局域各⾃独⽴,不同的域可以定义同名变量
- C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/ 类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响 编译查找逻辑,还会影响变量的⽣命周期,命名空间域和类域不影响变量⽣命周期。
- namespace只能定义在全局,当然他还可以嵌套定义。
- 项⽬⼯程中多⽂件中定义的同名namespace会认为是⼀个namespace(自动合并成一个),不会冲突。
- C++标准库都放在⼀个叫std(standard)的命名空间中。
#include<iostream>
using namespace std;
int a = 1000;
//这里的cat是命名空间的名字,⼀般开发中是⽤项⽬名字做命名空间名
namespace cat
{
// 命名空间中可以定义变量/函数/类型
int a = 100;
int ADD(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
//命名空间可以嵌套
namespace cat1
{
int a = 10;
}
}
int main()
{
printf("%d\n", a);
printf("%d\n", cat::a);
printf("%d\n", cat::cat1::a);
return 0;
}
2.3 命名空间的使用
编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间⾥⾯去查找。
- 指定命名空间访问,项⽬中推荐这种⽅式。
- using将命名空间中某个成员展开
-
#include<iostream> using namespace std; int a = 1000; namespace cat { int b = 100; namespace cat1 { int c = 10; } } using namespace cat; using namespace cat1; int main() { printf("%d\n", a); printf("%d\n", b); printf("%d\n", c); return 0; }
3. C++输入与输出
#include<iostream>
using namespace std;
int main()
{
int a = 100;
double b = 10.99;
char c = 'x';
//printf("%d %.2lf %c %s",a,b,c);
//在vs环境下<iostream>可能包含了<stdio.h>,所以这里没有包<stdio.h>也没有报错。
cout << a << " " << b << " " << c << endl;//输出
//std::cout << a << " " << b << " " << c << std::endl;
//C语言的输入和输出
scanf("%d %lf", &a, &b);
printf("%d %lf", a, b);
cin >> a;
cin >> b;
//or
cin >> a >> b;
//两种输入方式
cout << a << " " << b << " " << endl;
return 0;
}
- <iostream>是 Input Output Stream 的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输 出对象。
- std::cin 是 istream 类的对象,它主要⾯向窄字符(narrow characters (of type char))的标准输 ⼊流。
- std::cout 是 ostream 类的对象,它主要⾯向窄字符的标准输出流。
- std::endl 是⼀个函数,流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区。 • >是流提取运算符。(C语⾔还⽤这两个运算符做位运算左移/右移)
- 使⽤C++输⼊输出更⽅便,不需要像printf/scanf输⼊输出时那样,需要⼿动指定格式,C++的输⼊ 输出可以⾃动识别变量类型(本质是通过函数重载实现的,这个以后会讲到),其实最重要的是 C++的流能更好的⽀持⾃定义类型对象的输⼊输出。
- cout/cin/endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要 通过命名空间的使⽤⽅式去⽤他们。
- ⼀般⽇常练习中我们可以using namespace std,实际项⽬开发中不建议using namespace std。
-
#include<iostream> using namespace std; int main() { // 在io需求⽐较⾼的地⽅,如部分⼤量输⼊的竞赛题中,加上以下3⾏代码 // 可以提⾼C++IO效率 ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); return 0; }
4. 缺省参数
- 缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调⽤该函数时,如果没有指定实参 则采⽤该形参的缺省值,否则使⽤指定的实参,缺省参数分为全缺省和半缺省参数。(有些地⽅把 缺省参数也叫默认参数)
- 全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。
- 带缺省参数的函数调⽤,C++规定必须从左到右依次给实参,不能跳跃给实参。
- 函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值。
#include<iostream>
using namespace std;
void fn(int a = 10)
{
cout << a << endl;
}
int main()
{
fn();//没有传参时,使用参数的默认值,这里是10
fn(100);//传参时,使用指定的实参,这里是100
return 0;
}
#include<iostream>
using namespace std;
//全缺省
void fn1(int a = 10,int b = 100,int c = 1000)
{
cout << a << endl;
cout << b << endl;
cout << c << endl<<endl;
}
//半缺省
void fn2(int a, int b = 100, int c = 1000)
{
cout << a << endl;
cout << b << endl;
cout << c << endl << endl;
}
int main()
{
fn1();
fn1(5);
fn1(5,6);
fn1(5,6,7);
fn2(5);
fn2(5,6);
fn2(5,6,7);
return 0;
}
5. 函数重载
C++⽀持在同⼀作⽤域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者 类型不同。这样C++函数调⽤就表现出了多态⾏为,使⽤更灵活。C语⾔是不⽀持同⼀作⽤域中出现同 名函数的。
#include<iostream>
using namespace std;
int ADD(int left, int right)
{
return left + right;
}
double ADD(double left, double right)
{
return left + right;
}
int main()
{
cout << "int ADD(int left, int right)="<< ADD(5, 10) << endl;
cout << "double ADD(double left, double right)="<< ADD(5.1, 10.1) << endl;
return 0;
}
6. 引用
6.1 应用的概念和定义
引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间, 它和它引⽤的变量共⽤同⼀块内存空间。
#include<iostream>
using namespace std;
int main()
{
int a = 0;
//引用:b,c,d都是a的别名,所以本质都是上面那行代码开辟的空间
int& b = a;
int& c = a;
int& d = a;
//也可以给别名取别名,相当于e也是a的别名
int& e = b;
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
cout << &e << endl;
cout << a << endl;
cout << b << endl;
cout << c << endl;
cout << d << endl;
cout << e << endl;
++c;
cout << a << endl;
cout << b << endl;
cout << c << endl;
cout << d << endl;
cout << e << endl;
return 0;
}
6.2 引用的特性
- 引⽤在定义时必须初始化
- ⼀个变量可以有多个引⽤
- 引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体
#include<iostream>
using namespace std;
int main()
{
int a = 0;
int& b = a;
int c = 20;
b = c;
//这里并非是让b引用c,因为C++引用不能改变指向
//这里是一个赋值
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << a << endl;
cout << b << endl;
cout << c << endl;
return 0;
}
6.3 引用的使用
- 引⽤在实践中主要是于引⽤传参和引⽤做返回值中减少拷⻉提⾼效率和改变引⽤对象时同时改变被 引⽤对象。
- 引⽤传参跟指针传参功能是类似的,引⽤传参相对更⽅便⼀些。
6.4 const引用
- 可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访问权限在引⽤过程中可以缩⼩,但是不能放⼤。
- 一个表达式运算的结果,以及类型转换中会产⽣临时对象存储中间值,⽽C++规定临时对象具有常性,所以这⾥就触发了权限放⼤,必须要⽤常引⽤才可以。
- 所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象, C++中把这个未命名对象叫做临时对象。
#include<iostream>
using namespace std;
int main()
{
//1.典型的权限放大导致的错误引用
const int a = 10; //a是不能被改变的(权限较小)
//int& ra = a; //False,不能从const int转换成int&,
//(权限放大只读不能改的变成能读又能写的了)
const int& ra = a; //Right,(权限平移,只读不能写的变成只读不能改的)
//给a取别名为ra
//2.区分拷贝与权限放大
const int a = 10;//a是不能被改变的(权限较小)
int rd = a; //把a这块空间里的值拷贝到rd这块空间里,rd进行可读可改,并不会影响a里的值,所以不是权限放大的问题
//3.引用的时候权限可以缩小,但是不能放大
int b = 20; //b可读可改(权限较大)
const int& rb = b; //rb可读,不可被改(权限缩小),Right!
//这里引用是以缩小权限的方式来定义的,缩小权限的是rb,而b不受影响
//eg.
b++; //Right!
//rb++; //False!
//周树人以普通人时,权限小;鲁迅以文人时,权限大
//4.可以给常量取别名,但是一定要用const
const int& rc = 30;
//5.表达式的返回值存在临时对象里
a + b; //表达式的返回值存在临时对象里,临时对象具有常性,不能被修改
int rd = (a + b);//把临时对象拷贝给rb,临时对象具有常性,不能被修改
//int& rd = (a + b);//False! 这里rd变成可被修改的了
const int& rd = (a + b);//由于临时对象具有常性,所以引用时一定要用const
//6.隐式类型转换会产生临时对象
double d = 12.34;
int i = d;//把d的整型部分拿出来,放在一个int类型的临时变量里面,
const int& ri = d;//ri引用这个int类型的临时对象(不是引用的d),而临时对象具有常性
return 0;
}
6.5 指针和引用的关系
C++中指针和引⽤就像两个性格迥异的亲兄弟,指针是哥哥,引⽤是弟弟,在实践中他们相辅相成,功 能有重叠性,但是各有⾃⼰的特点,互相不可替代。
- 语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。
- 引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
- 引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。
- 引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。
- sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下 占4个字节,64位下是8byte)
- 指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。