目录
1.一个简单c++代码
#include<iostream>
using namespace std;
int main()
{
cout<<"hello world"<<endl;
return 0;
}
这个代码类似于我们学习c语言中“hello world”。
2.命名空间
2.1 域
域就是范围的意思,也是一个空间。它分为全局和局部,如
#include<iostream>
using namespace std;
int x=0;
int main()
{
int x=10;
return 0;
}
x=0就是在全局域里的变量,x=10是局部的变量。变量的调用是局部优先,局部没有的话就在全局域里去寻找调用。
2.2 自定义命名空间
当我们需要创建多个同名变量时,可以自命名多个域,即自定义空间。
#include<iostream>
using namespace std;
int x=0;
namespace name1//namespace+域名
{
int x=5;
}
int main()
{
int x=10;
return 0;
}
自定义空间里可定义变量和类,以及函数,甚至再定义一个空间。
定义多个同名空间:
#include<iostream>
using namespace std;
int x=0;
namespace name1//namespace+域名
{
int x=5;
}
namespace name1
{
int y=10;
x=1;
}
int main()
{
int x=10;
return 0;
}
定义多个同名空间,编译器会将他们整合成一个空间。
2.3 命名空间使用
我们要调用空间里的变量时,需要用::符号。
#include<iostream>
using namespace std;
int x=0;
namespace name1//namespace+域名
{
int x=5;
}
int main()
{
int x=10;
int y=name1::x;
return 0;
}
name1::x,::前跟空间名,::后跟命名空间里要调用的变量。
::前不跟空间名的话,默认的是在全局域里调用。
2.4 命名空间的展开
我们要使用命名空间里的变量时,需要使用::符号,可以将命名空间展开成全局空间。关键词using namespace +空间名。
#include<iostream>
using namespace std;
int x=0;
namespace name1//namespace+域名
{
int x=5;
}
using namespace name1;
int main()
{
int y=x;
return 0;
}
此时那么name1空间被展开成全局域,y=x时,局部没有变量x,在全局中有x=5.
命名空间局部展开:
#include<iostream>
using namespace std;
int x=0;
namespace name1//namespace+域名
{
int x=5;
int y=1;
int z=2;
}
using name1::z;
int main()
{
int n=z;
return 0;
}
如上代码using name1::z就是只把name1空间里面的z展开成为全局变量,其他仍在命名空间内。
3.c++的输入输出
我们看文章开头的代码:
#include<iostream>
using namespace std;
int main()
{
cout<<"hello world"<<endl;
return 0;
}
第一行#include<iostream>是C++中的头文件,它包含了我们需要输入输出的函数。
第二行using namespace std就是我们所讲的命名空间展开,std是iostream头文件里的命名空间。
cout<<"hello world"<<endl这个代码中cout类似于c语言中printf,<<是流插入符号,可以将变量类型输出到控制台中,end等同于'/n'就是换行。这个代码如果用c语言写就是printf("%s","hello world");printf需要说明变量类型,而<<可以自动识别变量类型。
int i,ch;
cin>>i>>ch;
这个代码类似于scanf,>>是流提取符号,cin>>就是从键盘中输入数据。cin也是std空间里的一个函数。
4.缺省函数
c++引入了一种缺省函数:
int Add(int a=0,int b=0)
{
return a+b;
}
缺省参数就是默认参数,当我们调用函数时不传参数,就默认参数值为我们设计的值:
int Add(int a=0,int b=0)
{
return a+b;
}
int main()
{
int c=Add();//return 0+0;
}
此时默认a和b是我们设定的值。
半缺省:
我们设计缺省函数时,可以部分参数缺省:
int Add(int a,int b,int c=0)
{
return a+b+c;
}
int main()
{
int c=Add();//return 0+0;
}
半缺省规则:缺省必须从右往左连续缺省,不能从左往右,也不能跳跃缺省。
半缺省函数的调用:传参时必须从左往右传,
nt Add(int a,int b,int c=0)
{
return a+b+c;
}
int main()
{
int c=Add(5,1);//a=5,b=1,c是缺省值默认为0;
}
5.函数重载
在c语言中不允许函数名相同,但c++中可以。
要求:函数名相同,参数不同,构成重载函数。
int Add(int a,int b)
{
return a+b;
}
double Add(double a,double b)
{
return a+b;
}
当我们调用函数时,系统会自动调用对应类型参数的重载函数。
int Add(int a,int b)
{
return a+b;
}
double Add(double a,double b)
{
return a+b;
}
int main()
{
Add(2,3);//调用int Add(int a,int b)
Add(2.1,2.2);//调用double Add(double a,double b)
}
6.引用
6.1 引用基础用法
6.1.1 引用的使用方法
引用即给变量起别名,用符号&:
int a;
int& b=a;
即b是a的别名,a和b是同一个变量,只是名字不一样:
通过a和b地址我们发现是相同的。
6.1.2 指针的引用
int* p;
int*& q= p;
即指针q是p指针的别名。
引用必需初始化,且不能改变指向:
int b;
int& a;
a=b;
引用时必须初始化,所以这个代码是错误的。
int b,c;
int& a=b;
a=c;
这个代码不是改变引用a指向c,而是将c赋值给a或者说是给b。
6.2 引用的常用方法
6.2.1 做函数的参数
void swap(int& x,int& y)
{
int s;
s=x;
x=y;
y=s;
}
int main()
{
int a=4;
int b=0;
swap(a,b);
}
x和y分别是a和b的引用,因此交换x和y,就是交换a和b。
6.2.2 引用的错误使用
临时变量出了作用域就销毁,不能引用返回。
int& func()
{
int a=0;
return a;
}
返回值是a的别名,a被销毁了,所以返回值是野值。
6.3 引用和指针对比
语法:
1.引用时别名,语法上不开空间,底层是用指针实现的要开空间;指针语法上需要开空间,底层要开空间。
2.引用必须初始化,指针可以不用初始化。
3.引用不可以改变指向,指针不可改变指向。
4.引用没有空引用,相对更安全;指针有空指针,容易出现野指针。
5.sizeof下引用是引用类型的大小;指针的大小32位下位4字节。
6.引用加加即引用的实体加一;指针加加即指针先后偏移一个指向类型的大小。
7.有多级指针,没有多级引用。
底层:
汇编层面上,没有引用,都是指针,引用编译后也转换成了指针。
7.内联函数
7.1宏函数
宏是一种替换,它是在预处理阶段进行的。
我们要写一个加法宏函数:
#define Add(a,b) ((a)+(b))
宏的缺点:
1.语法复杂,坑很多,不容易控制。
2.不能调试
3.没有类型检查
7.2 内联函数
函数在调用时,会建立栈帧,而内联函数实在调用的地方进行展开,没有建立栈帧的开销,因此提升程序运行速率。
关键词inline,在调用函数时,在函数前加上inline,就会在编译时用函数体替代函数调用。
void swap(int& x,int& y)
{
int s;
s=x;
x=y;
y=s;
}
int main()
{
int a=4;
int b=0;
inline swap(a,b);
/*
swap(a,b)
{
int s;
s=a;
a=b;
b=s;
} 被展开*/
}
缺点:函数展开会增加代码存储大小变大。
比如一个函数100行,调用1w次,内联函数占100*1w行,而不内联的话只占1w+100行。
特性:
1. 内联函数是以空间换时间的方法。
2. inline对于编译器而言只是一个建议:将函数规模较小,不是递归,且频繁调用的函数采用inline修饰,不满足的函数,编译器会忽略inline特性。
3. inline在编译器debug模式下不展开函数。
8. auto关键字
8.1 auto使用规则
1. auto定义变量时可以自动推导,赋值变量的类型。
int a;
void f1(int a,int b)
{
}
int main()
{
int a;
auto f=a;//f类型为
auto pf=&a;//pf类型为int*
auto& f=a;//f类型为int,是a的别名
void(*pf1)(int,int)=f1;//void(*pf1)(int,int)是函数指针,非常的复杂
auto pf1=f1;//pf1也是和上面一样的函数指针
}
2. 在同一行定义多个变量
void TestAuto()
{
auto a = 1, b = 2;
auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}
8.2 auto不能使用的场景
1. auto不能作为函数的参数
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}
2. auto不能直接用来声明数组
void TestAuto()
{
int a[] = {1,2,3};
auto b[] = {4,5,6};
}
9. 基于范围的for循环
9.1 范围for的语法
在c语言里我们遍历一个数组可以:
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
array[i] *= 2;
for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)
cout << *p << endl;
}
在c++里,for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。
int array[] = { 1, 2, 3, 4, 5 };
for(int a:array)
{
cout<<a<<endl;
}
这个代码,a为array赋值的变量,它从array数组下标为0开始遍历直到结束。
范围for配合auto的使用:
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array)
e *= 2;
for(auto e : array)
cout << e << " ";
return 0;
}
代码可以实现对array进行*2。
auto& e是对迭代范围的引用,因此对e修改就是对array修改;而auto e是对迭代范围值的拷贝,因此修改的话,只能修改e,不能修改array。
9.2 auto使用条件
1. for循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。
注意:以下代码就有问题,因为for的范围不确定
void TestFor(int array[])
{
for(auto& e : array)
cout<< e <<endl;
}
10. 指针空值nullptr(C++11)
10.1 C++98中的指针空值
NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:
void f(int)
{
cout<<"f(int)"<<endl;
}
void f(int*)
{
cout<<"f(int*)"<<endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
return 0;
}
程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。
在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。 注意:
1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。