前言
c++的发展史及c++能干什么不能干什么不是我们今天的重点,不在这里展开,有兴趣的朋友可以自行查阅相关资料。今天我们主要是围绕c++的入门程序,写一个“hello,world”,并且围绕这个入门程序简单介绍一下c++和c++的一些语法,做一个简单的入门
c++是兼容c的
我们以前在学c的时候也写过c的入门程序
#include <stdio.h>int main()
{printf("Hello,World");return 0;
}
在c++中这样写是完全支持的,但c++也有一套自己的输入输出体系。我们可以先简单看一下
#include <iostream>using namespace std;int main()
{cout << "Hello,World" << endl;
}
我们知道c++出现的背景就是为了解决c中不方便的部分。我们结合这个入门程序,看一下c中有哪些弊端,c++又是如何优化的
命名空间
在C语言中,有的时候我们会遇到一种情况
#include <stdio.h>
#include <stdlib.h>int rand = 10;int main()
{printf("%d", rand);
}
当我们运行这段代码时,我们发现报错了。我们可以来看一下报错信息
通过观察不难发现是因为rand重定义导致的。不对啊,我们只定义了一个全局的rand变量,怎么会有重定义的错误呢?原来是因为 stdlib.h 这个头文件会在编译时展开,这个头文件里包含了rand()函数导致重定义的问题。显然,这个问题在C语言中比较常见,也很隐蔽,我们不可能知道我们包含的头文件里有哪些被定义过的符号和名称,为了解决这个问题c++提出了命名空间的概念。
命名空间的关键字是namespace,我们使用的时候需要在namespace后面紧跟空间的名称,在用{}包裹空间,里面可以写变量和函数,不需要跟;号
namespace zzzyh {int a;int b();
}
//其中空间名称可以任意
使用时编译器默认会在局部和全局找,如果需要在命名空间内找需要使用 :: 指定
#include <iostream>
int a = 10;
namespace zzzyh {int a=20;int b();
}
using namespace std;int main()
{cout << zzzyh::a << endl;return 0;
}
这里的输入是20,如果没有指定zzzyh::,则会在全局变量里找,输出10
namespace只能定义在全局,可以嵌套定义,嵌套定义也需要嵌套使用
c++的标准库都是在std这个命名空间中
可以在文件的多个地方定义多个同名的命名空间,最终会被整合到一起
前面应该有朋友注意到了,为啥cout,endl是std里的方法,我们没有使用std:: 的方式访问,而是直接访问?这是因为命名空间可以使用 using展开,当全局变量使用,当然也可选择展开特定的变量和函数
输入输出
c++和c一样需要包和输入输出有关的头文件,不过c++的输入输出头文件是这样包的
#include <iostream>
完整格式
#include <iostream>int main()
{int i;std::cin >> i;std::cout << i << std::endl;
}
<iostream>是InputOutputStream的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输
出对象
std::cin是istream类的对象,标准输⼊流
std::cout是ostream类的对象,标准输出流
std::endl是⼀个函数,流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区
<<是流插⼊运算符,>>是流提取运算符。(C语⾔还⽤这两个运算符做位运算左移/右移)
c++的输入输出流相比于c而言简单了不少,这得益于c++实现自动识别类型,程序员不需要再输入输出中指定类型
如果再需要高性能的需求下,可以增加一下代码提高IO效率
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
缺省参数
缺省参数是c完全不支持,c++提出的概念,可以理解为在不知道具体传谁的时候为函数提供默认值。
在函数声明或者定义是,可以指定某些参数或者全部参数=某个值,如果后续在调用该函数时,对应的参数传参了则使用传入的参数,没有传参则使用默认的参数。可以分为全缺省和半缺省
规定半缺省只能从右往左连续缺省,不可跨越,这也是为了在编译时能准确确定调用的是哪个函数
传参也规定只能从左往右依次传入,不允许跨越,目的和上面一样方便编译时确定是哪个函数
如果函数的声明和定义,缺省值需要写在该函数声明处,函数定义处不写
函数重载
我们在c中,如果需要实现一个面对不同类型的加法函数往往需要起多个函数名来区分,函数重载就是为了结局这个问题的。函数重载要求函数名相同,函数的参数数量/顺序/类型不同,返回值不做要求。这样定义多个加法函数可以共用同一个函数名,但根据不同的函数参数来确定具体使用哪一个函数,降低使用成本,增加代码的可读性
引用
c中有指针的概念,就是开辟一块空间指向另一块空间,指向的空间存储被指向空间的地址,就叫指针,用指针间接操作被指向的空间
c++的引用我们认为是不开辟空间的,只是为这块空间取别名,通过别名直接操作原空间
类型& 引⽤别名 = 引⽤对象;
#include<iostream>
using namespace std;
int main()
{
int a = 0;
// 引⽤:b和c是a的别名
int& b = a;
int& c = a;
// 也可以给别名b取别名,d相当于还是a的别名
int& d = b;
++d;
// 这⾥取地址我们看到是⼀样的
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
return 0;
}
特性
引用必须初始化,一块空间可以有多个引用,引用不能再更改指向
使用
在使用上,我们常用在函数传参和函数返回值上,可以提高效率的同时更加方便的修改被引用对象。
引⽤和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代
const引用
可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访
问权限在引⽤过程中可以缩⼩,但是不能放⼤
在例如类型转换或者A*B这样的算式中,会有一个临时变量暂存结果的值,这个临时变量是具有常性的,可以理解为被const修饰
int main()
{
const int a = 10;
// 编译报错:error C2440: “初始化”: ⽆法从“const int”转换为“int &”
// 这⾥的引⽤是对a访问权限的放⼤
//int& ra = a;
// 这样才可以
const int& ra = a;
// 编译报错:error C3892: “ra”: 不能给常量赋值
//ra++;
// 这⾥的引⽤是对b访问权限的缩⼩
int b = 20;
const int& rb = b;
// 编译报错:error C3892: “rb”: 不能给常量赋值
//rb++;
return 0;
}
#include<iostream>
using namespace std;
int main()
{
int a = 10;
const int& ra = 30;
// 编译报错: “初始化”: ⽆法从“int”转换为“int &”
// int& rb = a * 3;
const int& rb = a*3;
double d = 12.34;
// 编译报错:“初始化”: ⽆法从“double”转换为“int &”
// int& rd = d;
const int& rd = d;
return 0;
}
inline
inline是一个内联函数的关键字,内联函数会在调用出展开,不建立栈帧以提高效率。但这也是建议编译器内联而已,具体内不内敛由编译器决定,内联一般需要函数体积足够小,调用足够频繁。这是为了替代c的宏函数。内联函数建议声明定义在一个文件中,否则容易造成链接错误
nullptr
在c/c++中的NULL是宏,在传统的C头⽂件(stddef.h)中,可以看到如下代码
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
c++的NULL定义为0,在实际使用中会混淆,因此引入nullprt关键字标识空指针
nullptr是⼀种特殊类型的字⾯量,它可以转换成任意其他类型的指针类型。使⽤nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,⽽不能被转换为整数类型
结语
以上便是今天的全部内容。如果有帮助到你,请给我一个免费的赞。
因为这对我很重要。
编程世界的小比特,希望与大家一起无限进步。
感谢阅读!