C++入门基础
一、C++的第一个程序
在创建 C++项目时,我们可以编写第一个C++程序。虽然C++兼容C语言绝大多数的语法,C语言实现的Hello World依旧可以运行,但在C++中需要将定义文件代码后缀改为.cpp,VS编译器看到是.cpp就会调用C++编译器编译,Linux下要用g++编译,不再是gcc。
规范的C++程序写法如下:
#include<iostream>
{
std::cout << << std::endl;
;
}
C++入门基础涵盖首个程序编写、命名空间机制、输入输出流、缺省参数规则、函数重载条件、引用特性及指针区别、内联函数优化以及空指针nullptr的使用。文章通过代码示例解析各语法点,强调命名隔离避免冲突、引用传参效率提升及类型安全等核心概念,帮助初学者建立正确的C++编程习惯。
在创建 C++项目时,我们可以编写第一个C++程序。虽然C++兼容C语言绝大多数的语法,C语言实现的Hello World依旧可以运行,但在C++中需要将定义文件代码后缀改为.cpp,VS编译器看到是.cpp就会调用C++编译器编译,Linux下要用g++编译,不再是gcc。
规范的C++程序写法如下:
#include<iostream>
{
std::cout << << std::endl;
;
}
这里std::cout和std::endl涉及命名空间的概念,下面详细分析。
在定义变量时难免会遇到名字冲突。例如库函数中的rand与自定义变量名冲突会导致报错。在C/C++中,变量、函数和类的名称都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
我们在使用标准库时,常在cout前加std::,这就是命名空间的使用方式。
定义命名空间需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
namespace本质是定义出一个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不再冲突。
#include<iostream>
using namespace std;
namespace jzx {
int rand = 10;
}
int main() {
cout << jzx::rand;
return 0;
}
C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找一个变量/函数/类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。
namespace只能定义在全局,当然它还可以嵌套定义。
#include<iostream>
using namespace std;
namespace jzx {
namespace game {
int grade = 100;
}
namespace work {
int time = 10;
}
}
int main() {
cout << jzx::game::grade << endl;
int x;
x = jzx::work::time;
cout << x << endl;
return 0;
}
项目中多文件中定义的同名namespace会认为是一个namespace,不会冲突。C++标准库都放在一个叫std(standard)的命名空间中。
编译查找一个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以要使用命名空间中定义的变量/函数,有三种方式:
#include<iostream>
using namespace std;
namespace jzx {
int grade = 100;
int time = 10;
}
int main() {
int x;
x = jzx::time; // 指定命名空间访问
return 0;
}
#include<iostream>
using namespace std;
namespace jzx {
int grade = 100;
int time = 10;
}
using jzx::grade;
int main() {
int x;
x = jzx::time;
int y = grade; // using将命名空间中某个成员展开
return 0;
}
#include<iostream>
using namespace std;
namespace jzx {
int grade = 100;
int time = 10;
}
using jzx::grade;
using namespace jzx;
int main() {
int x;
x = jzx::time;
int y = grade;
return 0;
}
std::cin是istream类的对象,它主要面向窄字符(narrow characters of type char)的标准输入流。std::cout是ostream类的对象,它主要面向窄字符的标准输出流。std::endl是一个函数,流插入输出时,相当于插入一个换行字符加刷新缓冲区。>>是流提取运算符。(C语言还用这两个运算符做位运算左移/右移)cout/cin/endl等都属于C++标准库,C++标准库都放在一个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们。using namespace std,实际项目开发中不建议using namespace std。#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main() {
int i;
char ch;
double d;
cin >> i >> ch >> d;
cout << i << ' ' << ch << ' ' << d << endl;
scanf("%d %c%lf", &i, &ch, &d);
printf("%d %c %.2lf", i, ch, d);
return 0;
}
这样我们在使用的时候就顺手多了,但是当我们想要控制输出的几位小数或者占多少位置的时候我们C++也可以实现但是太过于麻烦,相比之下我们还是使用C语言的。
#include<iostream>
using namespace std;
void fun(int a = 0) {
cout << a << endl;
}
int main() {
fun(); // 缺省
fun(111);
return 0;
}
全缺省示例:
#include<iostream>
using namespace std;
void fun(int a = 0) {
cout << a << endl;
}
void fun1(int x = 1, int y = 2, int z = 3) {
cout << x << " " << y << ' ' << z << endl;
}
int main() {
fun(); // 缺省
fun(111);
fun1();
fun1(11, 11, 11);
return 0;
}
如果我们这样定义函数:
void fun1(int x = 1, int y, int z = 3) {
cout << x << " " << y << ' ' << z << endl;
}
编译器将会报错,因为我们跳跃式的给缺省参数。我们必须从右到左的给缺省值。
int add(int x = 1, int y = 1);
int add(int x = 1, int y = 1) {
return x + y;
}
当我们给函数的声明和定义都给了缺省值的时候我们的编译器也会报错。
C++支持在同一作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调用就表现出了多态行为,使用更灵活。C语言是不支持同一作用域中出现同名函数的。
#include<iostream>
using namespace std;
// 参数类型不同
int add(int x, int y) {
return x + y;
}
double add(double x, double y) {
return x + y;
}
// 参数的个数不同
void f() {
cout << "f()" << endl;
}
void f(int x) {
cout << "f()" << endl;
}
int main() {
return 0;
}
但是返回值不同的不能作为函数的重载。
还有一种特例:
void f1(int x = 1) {
cout << 'f' << endl;
}
void f1() {
cout << "f()" << endl;
}
int main() {
f1();
return 0;
}
因为第一个函数重载了,所以在调用函数的时候就会产生歧义,导致报错。
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
类型&引用别名=引用对象; C++中为了避免引入太多的运算符,会复用C语言的一些符号,比如前面的>,这里引用也和取地址使用了同一个符号**&**,大家使用方法角度区分就可以。
#include<iostream>
using namespace std;
int main() {
int x = 10;
int& a = x;
int& b = x;
cout << &a << endl;
cout << &x << endl;
cout << &b << endl;
return 0;
}
在这里a ,b就是x的别名,他们的地址是相同的。
我们来对其中一个进行操作:
我们可以看到这个对他的别名的操作是会影响全部的,这实际上很容易理解,因为周树人和鲁迅都是同一个人。
#include<iostream>
using namespace std;
int main() {
int a = 10;
// 编译报错:'ra': 必须初始化引用
//int& ra;
int& b = a;
int c = 20;
// 这里并非让b引用c,因为C++引用不能改变指向,
// 这里是一个赋值
b = c;
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
return 0;
}
可以引用一个const对象,但是必须用const引用。const引用也可以引用普通对象,因为对象的访问权限在引用过程中可以缩小,但是不能放大。
因为我们本来的n就是只读不能修改的,你试想通过一个引用来改变这个值是不允许的,我们只能也用const的引用才行。
int b = 10;
const int& a = b;
这样是可以的,这就是我们所说的权限的缩小。
需要注意的是类似 int& rb = a*3; double d = 12.34; int& rd = d; 这样一些场景下a*3的和结果保存在一个临时对象中,int& rd = d也是类似,在类型转换中会产生临时对象存储中间值,也就是时,rb和rd引用的都是临时对象,而C++规定临时对象具有常性,所以这里就触发了权限放大,必须要用常引用才可以。
类型转化的时候也会创建临时常量。
C++中指针和引用就像两个性格迥异的亲兄弟,指针是哥哥,引用是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有自己的特点,互相不可替代。
因为我们在写宏定义的时候太过于繁琐,我们还容易写错,所以我们就可以使用C++中的inline函数。
当我们的代码量小的时候他会直接展开,不建立栈帧,当我们的代码量大的时候他还会像普通的函数一样进行。
#include<iostream>
using namespace std;
inline int add(int x, int y) {
return x + y;
}
int main() {
const int n = 10;
//int& x = n;
const int& x = n;
int b = 10;
const int& a = b;
const int& y = b * 2;
return 0;
}
在我们的inline函数的帮助下我们的宏定义就实现了进化,因为这个简单的a+b的问题在我们的宏定义中我们需要这么写:
#define add(x,y) ((x)+(y))
我们太容易丢失括号了。所以我们就舍弃了他。
所以我们在C++中的空指针变成了nullptr。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online