C++的入门文档(命名空间、输入输出、缺省函数、函数重载、引用、内联函数、auto、范围for、空指针)

在这里插入图片描述

今天浪子带大家来初步了解一下c++的入门的几个小特性也算是小语法,可以加深后面对类和对象的学习的理解。

C++的命名空间(namespace)

这关键字的表面意思就是命名空间的意思,那么什么是命名空间呢?命名空间又会有什么用呢?

命名空间就是下面代码中的namespace code,我自己定义了一个code的命名空间,又叫域,定义的这个域中又定义了两个变量,然后我在下面的使用的时候就要指定我这个变量是属于那个域的,因为编译的时候会先查找局部域中有没有这个变量没有的话就会报错,但是我们在变量名之前加上了code::,其中(::)是域作用限定符,用来限定这个变量是那个域的。

那么这时候我们就引出来了namespace的作用,我们知道C语言中是不能解决命名冲突的,如果使用了和库函数名相同的变量名在使用这个库函数之后链接的时候就会出现问题,这也限制了我们在用C语言编写大型工程的时候多人协同难免会出现同名变量,我们如果是在不同的文件可以用static修饰这个变量,略显麻烦,于是c++就进行了改进引入了变量空间这个关键字。

#include<iostream>

namespace code
{
    int cout;
    int cin;
}
int main()
{
    std::cin>>code::cout>>code::cin;
    std::cout<<code::cout<<code::cin<<std::endl;
}

关于namespace,namespace是可以嵌套定义的,那么嵌套定义有什么意义呢?看下面这段代码

namespace a
{
    namespace net
    {
        int a;
        int b;
        ...
    }
    namespace os
    {
        int a;
        int b;
        ....
    }
}

比如我们这个a公司和别的公司合作,最后代码要整合所以我们a公司就需要一个自己的namespace,而a公司里面又分有不同的部门,比如net网络部门,负责网络部分,os部门负责系统部分,这些部门也需要不同的命名空间,所以这就是嵌套调用的作用,当然namespace里面也可以定义函数,结构体,并不是只有变量。关于怎么使用来看下面

int main()
{
    std::cin>>a::os::a;
    std::cin>>a::net::a;
}

这里我们就完成了使用,意思是从键盘上输入到os中的a,从键盘上输入到net的a。

关于域的说明,c++中有,局部域,全局域,类域,命名空间域等等。其实c语言也有关于域的说明,只是使用的比较少来看代码

int a = 100;
int main()
{
    int a = 20;
    printf("%d\n",a);//1
    printf("%d\n",::a);//2
}

看上面这段代码第一个输出的是什么?答案是20,因为我们知道在C语言局部变量和全局变量重名的时候局部变量优先,也即是就近原则。但是第二个输出的是什么呢?答案是100,为什么呢?因为我们这里用了,域作用限定符(访问指定的域)而作用符前是空,那就是访问全局域,也即是我们的全局变量a所在的域。

我们使用namespace,定义了一个域,不同域之间没有联系,也就是不同域的变量可以重名,但是同一个域内还是不可以重名哦
当我们在同一个工程的不同文件中定义了同名的命名空间,在链接的时候这些同名的命名空间会被合到一起。

C++的输入输出

先来看一段程序

#include<iostream>
using namespace std;
int main()
{
    cout<<"hello world<<endl;
}

这段程序会输出hello world
using namespace std; 这一句是什么意思呢?这里要说到,c++为了防止命名冲突,所以将所有的标准库的命名定义在了一个命名空间内也就是std,这里的意思是将std这个标准库的命名空间全部展开,也就是标准库的所有命名都在这里,所以下面就不用使用std::了但是这并不是一种好的写法,特别是在项目中,这是错误的,但是我们在日常练习的时候可以这也写。

cin 其实是istream类的对象,cout 是ostream类的对象,i就是输入的意思,o就是输出的意思,所以他们并不属于函数,关于类和对象我会在下面给大家介绍。这里知道即可。

cout cin是具有自动识别类型的功能的,也就是我们在输入输出的时候不需要用以前C语言的限定符,%d,%f,%s,等等,cout<<填入变量<<endl;填入变量就会自动识别类型然后输出到控制台,至于箭头的意思我理解的是代表数据的流向,从变量流向了控制台,endl的意思其实就是换行符的意思,当然也可以换种写法cout<<"hello kisskernel<<'\n'; 直接写也可以换行。cin>>arr 这里的箭头方向意思是数据从控制台流向了arr这个变量。

我们知道cpp文件里面我们也是可以写C语言的代码的,这就是说我们在编码的时候可以c/c++混用,比如输入输入我们可以写printf也可用cout,看情况选择最合适的即可,cout可以自动识别类型确实是很爽,但是cout也有蛋痛的时候,比如下面这段代码

struct stu
{
    char name[20];
    int id;
};
int main()
{
    struct stu s1 = {"kisskernel",1111110974};
    std::cout<<"name:"<<s1.name<<"id:"<<stu.id<<endl;
    printf("name:%s\nid:%d\n",stu.name,stu.id);
}

相信这里我们就理解了什么时候该用cout,和printf了把,就是根据情况而定,用cout太复杂我们就用printf啦。

标准库使用的三种方式

//1.用std::,域作用限定符来访问
std::cout<<"hello kisskernel"<<std::endl;
//缺点是麻烦,每次使用都要加上std::,但是这是最标准的写法

//2.将std标准库全部展开
using namespace std;
//这种方式在后面调用是很方便,但是原本为了防止命名冲突引入的命名空间就没用了。
//只能在日常练习中使用。

//3.将std标准库部分展开
//也就是只展开我们常用的对象,其他的不展开可以防止命名冲突。
using std::cout;
using std::cin;
using std::endl;
//这里我们展开了这三个最常用的,后面使用的时候就不用加std::了,这种写法在工程中很常见。

缺省函数

void add(int x,int y = 10)
{
    return x+y;
}
int main()
{
    std::cout<<add(20)<<std::endl;
    std::cout<<add(10,30)<<std::endl;
}

这里的第一次函数调用只传了一个实参,我们的函数定义的第二个参数是缺省参数,所以没有传值的时候y默认为10,打印出来就是30,第二次调用,打印的就是40.
缺省函数的参数只能是从右向左缺省,不可以从左向右缺省,因为我们传参的时候就是从左向右传参的,没办法避开左边的参数直接给右边的参数传参。并且缺省的参数必须是从右向左是连续的,这里对于参数的缺省有两种1.全缺省,就是函数的所有参数都是缺省的。2.半缺省,就是左边有不缺省的参数,右边有连续的缺省的参数。并不一定是一半一半的,上面的就是半缺省

注意缺省量必须是常量比如上面的int y = 10,也不一定是int类型但是是常量就行,一般来讲我们只需要在函数定义或者函数声明中指定缺省参数即可,如果两个都指定,那么指定的缺省参数必须是相同的虽然不会报错,但是会引发混淆,一般情况我们只需要在函数的声明中指定缺省参数即可。

缺省值一般是常量或者全局变量。

int add(int x,int y)
{
    return x+y;
}
int main()
{
    int add(int x = 10,int y = 20);
    std::cout<<add()<<std::endl;
}

这时候我们打印的就是30,传参数就打印我们传参的和。
缺省函数的作用,让函数的调用更加灵活,比如在数据结构中简化代码如下

#include<iostream>
using namespace std;

struct stack
{
    int*a;
    int top;
    int capacity;
}
void stackInit(struct stack*s,int size = 4)
{
    s->a = (int*)malloc(sizeof(int)*size);
    s->top = 0;
    s->capacity = 0;
}
int main()
{
    struct stack s1;
    stackInit(&s1);//这里我不传第二个参数默认初始化空间为4
    //但是如果已知栈最小为100
    stackInit(&s1,100);//这样就可以减少栈空间不足多次开辟内存的性能消耗
}

函数重载

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题

c的函数重载就是让一个函数名具有双层意义。
我们知道C语言中函数名是不可以相同的,但是c引入函数重载之后函数名可以相同,但是重载有条件,就是函数名相同,但是参数的类型不同,或者参数的个数不同,注意仅仅函数的返回类型不同是不行的。但是参数的类型有一个不相同就可以。

#include<iostream>
using std::cout;
using std::cin;
using std::endl;

int add(int x, int y)
{
	return x + y;
}
double add(int x, double y)
{
	return x + y;
}

int main()
{
	cout << add(20, 30.3) << endl;
}

上述代码是可行的。
函数重载可以根据我们传参的类型不同(自动识别)调用不同的函数,这样看似是调用一个函数其实调用了不同的函数。

关于缺省和函数重载的一个错误代码
实际上缺省和重载没有直接的练习,但是我们有时候会想我们将一个缺省函数和一个不同函数重载,那么调用的时候会调用那个函数呢?看代码

#include<iostream>
using std::cout;
using std::cin;
using std::endl;

int add(int x, int y)
{
	return x + y;
}
int add(int x, int y,int z = 10)
{
	return x + y + z;
}

int main()
{
	cout << add(10, 20, 39) << endl;
	cout << add(10, 20) << endl;
}

这里我们可以看到,两个add函数满足重载条件,也就是参数的个数不同,我们在调用的时候,第一个传三个参数,可以正常调用,第二个函数输出69,但是第二个调用,看似是缺省函数调用,省略掉了那个z的传参,但是编译器在这里就混乱了,于是就会报错,但是重载是满足条件的,这也说明了重载和缺省函数没有直接的联系。

这里引出一个问题,为什么c支持函数重载,C语言不支持呢?那c底层又是如何支持函数重载的?

这里就要说到一个程序编译的过程了。
1.首先是预处理,这个阶段会完成头文件展开,注释删除,宏替换,条件编译。
2.然后是编译,这个阶段是检查语法错误,生成汇编代码。一般语法报错都是在这里检查出来的。
3.汇编,这里是将汇编代码转换成二进制机器码。(计算机只能识别这个)
4.链接,根据生成的符号表,将程序链接到一起形成可执行程序。

这里可以看到在链接的时候会根据符号表来找到声明的或者调用的函数,这时候如果没有找到就会发生链接错误一般windows上是LNIK前缀的错误。

所以不管C语言还是C++都有一套自己的函数名修饰规则C语言是直接将函数名作为修饰规则,链接的时候直接去编译生成的符号表里面去找,找不到会报错,C语言链接错误.png这里可以看到无法解析的外部符号_add,这个就是C语言在符号表中生成的。

而c++的函数名修饰规则是与C语言不一样的,这里给出的规则是Linux上的修饰规则

int add(int x,int y);
//上面这个函数在符号表中会被修饰成
_z3addii()
其中_z是前缀3代表函数名字符数,ii代表参数类型
如果后面是dd那就表示double double类型的参数。

如果是windows上修饰规则就比较诡异了,Windows使用的是各种符号来表示C++语言链接错误.png

这个不需要深入探究,从上面的函数名修饰规则我们可以看得出,c++能实现引用根本原因是同名函数在函数表中也会被修饰成不同名的,可以看到返回值类型是没有作为修饰选项的,所以函数重载一定要满足参数个数或者类型不同,才可以修饰出不同的函数名实现重载。

extern "C"void cfree();

可以看到上述这段代码中的extern “C”,这段代码的作用就是声明该函数用C语言的函数名规则编译和查找。一般使用在中间件函数中,中间件一般用c实现但是为了实现在C语言和c中都可调用所以就用来extern "c"这个符号。

同时如果一个函数加上了extern “C”,那么这个函数就是不可重载了,因为用的C语言的函数名修饰规则自然不支持重载。

引用

引用的概念

这里引用又是别名的意思,就比如一个人有一个姓名,但是通常朋友或者舍友会给他有一个外号,这两个名字都是指向的同一个人,这就是引用和变量之间的关系。

引用看似和指针类似,但是指针可以不初始化,只会报警告,引用如果不初始化就会报错,因此引用在定义的时候必须初始化。

引用和指针的区别

引用和指针的区别

这里还要注意一个问题,那就是引用初始化后,它所指向的对象就不可以再改变了,这里要和指针区别开来,指针初始化之后是可以改变它里面存放的地址的,也就是可以改变让它指向别的对象,但是引用不可以,引用只能指向初始化的对象,后续对引用的改变就是对引用初始化的对象进行改变

引用的使用场景

1.做参数
int main()
{
    int a = 10;
	int b = 20;
	int& c = a;
	c = b;
}

这里对c的改变是把b的值赋值给a,查看地址,c和a的地址是一样的,并没有使c成为b的引用。


int a = 10;
int&b = a;

b = 30;

这就是引用的用法,这里的b就是a的别名,我们修改b,a也会跟着改变,a和b指向同一块内存空间。
关于引用的作用我们来看这一段代码

void swap(int& x,int& y)
{
    int tmp = x;
    x = y;
    y = tmp;
}
int main()
{
    int a = 10;
    int b  =20;
    swap(a,b);
}

我们知道C语言中交换变量必须要传地址,但是有了引用就会简单很多,我们可以直接传变量,就能实现a和b的交换。因为参数的x,是a的别名所以x的类型其实也是int。因此,下面这段代码不构成函数重载

void swap(int& x,int& y)
{
    int tmp = x;
    x = y;
    y = tmp;
}
void swap(int x,int y)
{
    int tmp = x;
    x = y;
    y = tmp;
}

引用可以作为参数带回返回值,代替原来的指针的作用,可以让函数带回来两个返回值甚至更多,代码如下

#include<iostream>
using namespace std;
void cmp(int* arr,int&returnsize)
{
    ...;
}
int main() 
{
    int arr[] = {1,2,4,4,5,32,4,25,2342,33,2,3};
    int cnt = 0;
    int&rcnt = cnt;
    cmp(arr,rcnt); 
}

上面这段代码,只要在cmp函数内改变*returnsize就会改变外面的cnt.相当于函数多了一个返回值。

2.做返回值

某个函数的返回值可以是一个引用,但是前提是返回的这个引用的实体,出了函数的作用域不会被销毁,比如static类型变量或者全局变量。

int& Count()
{
 static int n = 0;
 n++;
 // ...
 return n; }

下面看一段,引用做返回值的错误使用

int& Add(int a, int b) {
 int c = a + b;
 return c; }
int main()
{
 int& ret = Add(1, 2);
 Add(3, 4);
 cout << "Add(1, 2) is :"<< ret <<endl;
 return 0; }

这段代码最后打印的是7,实际上已经发生了越界访问了。第一次ret保存了以及还给了操作系统的c的引用,也就是我们可以还可以通过这个ret访问或修改c,可以类比C语言的野指针,第二次调用add函数c的值被改成7,当然这个最后打印出来7是因为操作系统回收了内存但是并没有清空内容,如果你再次调用一个别的函数这时候就是随机值啦。因为其他函数会占用这个栈帧位置。所以c那个位置的值就是随机的了。

这里说到越界不会报错,这个是因为,操作系统对于越界的检测并不是非常严格的,一般越界读数据不会报错,越界写可能会报错越界写的时候如果写入的位置是编译器的检查位,就会报错,这个就像是抽查一样,至于为什么抽查,可以想一想如果每个位置都检查那效率是不是很低啊,所以编译器只会在你越界可能性最大的地方设置检查位。

引用做参数的效率

在为函数传参的时候,如果传递的对象比较大,建议使用传引用传参效率会更高,因为传值传参的时候每次都不是直接将这个大的变量传过去的,而是在内存中新拷贝一份内容然后传过去。效率非常低。

#include <time.h>
struct A{ int a[10000]; };
void TestFunc1(A a){}
void TestFunc2(A& a){}
void TestRefAndValue()
{
 A a;
 // 以值作为函数参数
 size_t begin1 = clock();
 for (size_t i = 0; i < 10000; ++i)
 TestFunc1(a);
 size_t end1 = clock();
 // 以引用作为函数参数
 size_t begin2 = clock();
 for (size_t i = 0; i < 10000; ++i)
 TestFunc2(a);
 size_t end2 = clock();
 // 分别计算两个函数运行结束后的时间
 cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
 cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl; }

这段代码可以看出来传引用的效率是比传值效率高的

同样返回值这里也是如此,如果返回的对象比较大,也建议使用返回引用的防止,因为这个变量在返回的时候也是相当于在内存中先拷贝一份然后返回这个拷贝。效率不高

#include <time.h>
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }
void TestReturnByRefOrValue()
{
    // 以值作为函数的返回值类型
    size_t begin1 = clock();
    for (size_t i = 0; i < 100000; ++i)
        TestFunc1();
    size_t end1 = clock();
    // 以引用作为函数的返回值类型
    size_t begin2 = clock();
    for (size_t i = 0; i < 100000; ++i)
        TestFunc2();
    size_t end2 = clock();
    // 计算两个函数运算完成之后的时间
    cout << "TestFunc1 time:" << end1 - begin1 << endl;
    cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

指针和引用的区别

首先在语法上,引用是没有内存空间的,他只是对这个变量又新取了一个名字。
而指针是创建一个四个字节的指针变量来保存变量的地址。

但是在底层实现上其实引用也是用的指针。

int main()
{
	int a = 10;

	int& ra = a;
	ra = 20;

	int* pa = &a;
	*pa = 20;

	return 0;
}

引用和指针底层实现.png

可以看到在汇编代码的底层实现中引用其实是和指针一样的,只是外面我们经过处理,吧指针变成了引用,因为引用更安全,更方便。

引用和指针的不同点:

  1. 引用在定义时必须初始化,指针没有要求
  2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  3. 没有NULL引用,但有NULL指针
  4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  6. 有多级指针,但是没有多级引用
  7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  8. 引用比指针使用起来相对更安全

内联函数(inline)

就是用inline修饰的函数,编译器会在调用这个函数的地方展开,这样就没有参数压栈的开销,可以加快程序执行的速度。提升运行效率一般只有调用次数多的小函数会被设置成内联函数

内联函数的特性

内联函数,实际上是用空间换时间的一种做法,小函数在这里展开就会多占用一块空间,如果不是内联函数,只需要不断重复调用一块函数即可

举例子:一个inline函数有100条执行语句,程序中有十个地方调用了这个inline函数,那么总共在程序中添加了1000条语句,如果这个函数不是inline函数,那么只需要110条语句就可以完成了。所以一般情况inline只能用在多次调用的小函数上。

如果代码中有循环或者递归是不能成为内联函数的。
inline只是一个建议性关键字,就像register一样,编译器会评估这个函数适不适合作为内联函数,就是编译器可以自己决定接不接受你的建议
inline不建议声明和定义分离(一个在cpp文件,声明在.h文件),分离会导致链接错误。因为inline被展开,就没有函数地址了,链接的时候就会找不到。

// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i) {
 cout << i << endl; }
// main.cpp
#include "F.h"
int main()
{
 f(10);
 return 0; 
}

//这里inline函数是没有地址的所以找不到函数体就会在链接的时候报错

image.png

c++的inline出现是为了解决C语言的宏的缺陷问题的

宏的缺陷

1,宏不能调试
2,宏书写起来很复杂容易出错
3,宏没有类型安全的检查

宏的优点

1,可以增强代码的可复用性
2,可以提高程序的性能
宏的原理就是替换,在预处理阶段将调用宏的地方替换成宏的那段代码,和inline实际是差不多的但是inline更好一些。

C++11引入的auto关键字

auto 的使用规则

我们能知道在C语言中的auto关键字是使用auto修饰的变量,是具有自动存储器的局部变量,但是很少有人使用。

C++中的auto被加上了自动推导类型的作用,也即是在一个表达式中,auto类型的变量可以根据给他赋值的变量的类型来推导这个被赋值的变量的类型。

官方语言:C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型 指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得

int TestAuto()
{
 return 10; }
int main()
{
 int a = 10;
 auto b = a;
 auto c = 'a';
 auto d = TestAuto();
 
 cout << typeid(b).name() << endl;
 cout << typeid(c).name() << endl;
 cout << typeid(d).name() << endl;
 
 //仅定义一个auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
 return 0; 
}

auto这个自动推导类型的关键字主要使用来简化代码的,如果是比较长的那种类型,我们可以直接用auto,让编译器根据给他赋值的对象来初始化auto的类型。

官方解释:【注意】
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类 **
。因此
auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型**。

int main()
{
	int x = 10;
	auto a = &x;//取出来的是地址所以自动初始化成指针
	auto* b = &x;//加*指定初始化成指针
	auto& c = x;//如果是引用要加&,否则相当与初始化成int而不是引用
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	*a = 20;
	*b = 30;
	c = 40;
	return 0;
}

上面这段代码就展示了auto的使用,有时候为了防止混淆可以在auto后面加上一个*或者&来规定auto转换成什么类型。特别是引用一定要加&。否则相当于定义了一个int c的变量。

void TestAuto()
{
	auto a = 1, b = 2;
	auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}

如果想要一行定义多个变量,这多个变量的类型必须是相同的,因为编译器只会根据第一个变量的类型来推导,然后用推导出来的类型取定义剩下的变量。

auto的错误使用

// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}

void TestAuto()
{
 int a[] = {1,2,3};
 auto b[] = {456};
}

1,首先auto不能作为参数,因为auto推导类型是在编译阶段,这时候参数还没有进行链接,无法知道有什么类型的参数传给这个auto,所以编译器无法推导a的类型。
2,auto不能用来声明数组类型
3,为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法
4,auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环,还有lambda表达式等
进行配合使用。

C++11的范围for

如果我们要遍历一个数组,C++11中给我们提供了一种简单的写法,范围for

常规写法是这样的如下被注释掉的:

int main()
{
	int arr[] = { 1,2,3,4,5,6,78,86,5,4 };
	//for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	//{
	//	cout << arr[i] << endl;
	//}
	for (auto e : arr)
		cout << e << endl;
	return 0;
}

没有被注释的就是范围for的写法,这样是不是很简洁,但是当我们想要改变数组的内容的时候看如下代码:

int main()
{
	int arr[] = { 1,2,3,4,5,6,78,86,5,4 };
	for (auto e : arr)
		e *= 2;
	for (auto i : arr)
		cout << i << " ";
	return 0;
}

如上我们可以看到打印出来的没有变化,这是为什么呢?因为这里auto e相当书是arr数组每个元素的一个拷贝,就像函数传参哪里一样,如果你没有传变量的地址或者引用,是不能改变函数外的变量的。形参的改变不会影响到实参。

int main()
{
	int arr[] = { 1,2,3,4,5,6,78,86,5,4 };
	for (auto& e : arr)
		e *= 2;
	for (auto i : arr)
		cout << i << " ";
	return 0;
}

只需要稍微修改就可以啦。

范围for的使用条件

1,当前只需要了解,范围for需要数组有明确的范围就可以了。对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的 方法,begin和end就是for循环迭代的范围。
提供一段错误代码

void TestFor(int array[])
{
 for(auto& e : array)
 cout<< e <<endl; 
}

这里的array只是一个int*的指针,无法通过这个指针得知数组的第一个元素和最后一个元素的范围

2,迭代的对象要实现++和==的操作。(关于迭代器这个问题,以后会讲,现在大家了解一下就可以了)

C++11中的空指针nullptr

我们知道在以前写C语言的时候,空指针是NULL,但是NULL其实使用宏定义的0

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

这种定义方式在大多数的情况下都没有问题,但是有时候会出现问题,比如:

void f(int) {
 cout<<"f(int)"<<endl; }
void f(int*) {
 cout<<"f(int*)"<<endl; }
int main()
{
 f(0);
 f(NULL);
 return 0; 
}

这里可以看到将这个NULL直接当作参数打印出来的是f(int),但这不是我们想要的,NULL应该是零地址。
C++中的nullptr相当于也是个0,但是是指针类型的0,不论我们当成地址打印还是用上述代码判断都没有问题,所以在以后的代码中空指针尽量使用nullptr。

注意:

  1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的
  2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
  3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在C基础上[2],一九八三年又由贝尔实验室的Bjarne Strou-strup推出了C++。 C++进一步扩充和完善了C语言,成为一种面向 对象的程序设计语言。C++目前流行的编译器最新版本是Borland C++ 4.5,Symantec C++ 6.1,和Microsoft Visual C++ 2012。C++提出了一些更为深入的概念,它所支持的这些面向对象的概念容易将问题空间直接地映射到程序空间,为程序员提供了一种与传统结构程序设计不同的思维方式和编程方法。因而也增加了整个语言的复杂性,掌握起来有一定难度。 C++由美国AT&T贝尔实验室的本贾尼·斯特劳斯特卢普博士在20世纪80年代初期发明并实现(最初这种语言被称作“C with Classes”带类的C)。开始,C++是作为C语言的增强版出现的,从给C语言增加类开始,不断的增加新特性。虚函数(virtual function)、运算符重载(Operator Overloading)、多重继承(Multiple Inheritance)、模板(Template)、异常(Exception)、RTTI、命名空间(Name Space)逐渐被加入标准。 C++ 1998年国际标准组织(international standard organization, ISO)颁布了C++程序设计语言的国际标准ISO/IEC 1988-1998。C++是具有国际标准的编程语言,通常称作ANSI/ISOC++。 1998年是C++标准委员会成立的第一年,以后每5年视实际需要更新一次标准。C++0x最终国际投票已于2011年8月10日结束,并且所有国家都投出了赞成票,C++0x已经毫无疑义地成为正式国际标准。先前被临时命名为C++0x的新标准将被称为C++ 2011。C++ 2011取代现行的C++标准ISO/IEC 14882,它公开于1998年并于2003年更新,通称C++98以及C++03。国际标准化组织于2011年9月1日出版发布ISO/IEC 14882:2011,名称是:Information technology -- Programming languages -- C++ Edition: 3。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KissKernel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值