目录
一 C++的发展历史
-
起源:
斯特劳斯特鲁普在贝尔实验室工作时,为解决大型程序开发的效率问题,希望在 C 语言基础上增加面向对象特性(如类、继承),最初将其命名为 “C with Classes”,作为 C 语言的扩展。 -
正式命名与标准化:
1983 年正式更名为 C++(“++” 体现对 C 的增强),并逐步加入多态、模板、异常处理等特性。1998 年,ISO 发布首个 C++ 标准 C++98,奠定语言的核心框架。 -
后续标准迭代(2003 - 至今):
- 2003 年:发布 C++03,主要对 C++98 进行 bug 修复。
- 2011 年:C++11 发布,引入自动类型推导(
auto
)、Lambda 表达式、智能指针等重大特性,被视为 “现代 C++” 的起点。 - 后续每隔 3 年更新一次:C++14(完善 C++11)、C++17(增加并行算法等)、C++20(引入模块、协程等)、C++23(进一步优化特性),持续增强语言的灵活性和性能。
-
核心特点:
始终保持与 C 语言的兼容性,同时融合面向对象编程、泛型编程等范式,兼顾底层控制能力与高层抽象,广泛应用于系统开发、游戏引擎、嵌入式等领域。
二 C++的第一个程序
在C语言中的第一个程序是写出Hello world
#include<stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
在C++中写出这个程序如下。
注:C++兼容C语⾔绝⼤多数的语法,所以C语⾔实现的hello world依旧可以运⾏,C++中需要把定义文件 代码后缀改为.cpp,vs编译器看到是.cpp就会调⽤C++编译器编译,linux下要用g++编译,不再是gcc
#include<iostream>
//引入标准输入输出流库。借助这个库,程序才能够进行数据的输入和输出操作
using namespace std;
//使用标准命名空间
int main()
{
cout << "hello world\n" << endl;
//向标准输出(一般就是控制台)打印信息
return 0;
}
其中的
cout << "hello world\n" << endl;
cout
是标准输出流对象。<<
是流插入运算符,用于把数据插入到输出流中。"hello world\n"
是要输出的字符串,\n
是换行符。endl
同样代表换行,不过它还会刷新输出缓冲区,保证信息能立即显示出来。
<<和 >>:
1. 位运算(C 和 C++ 通用)
<<
和>>
作为位运算符时,用于对整数的二进制位进行左移或右移操作,两者在 C 和 C++ 中的行为完全一致。左移运算符
<<
- 功能:将左侧操作数的二进制位向左移动指定的位数,右侧空出的位用
0
填充。- 语法:
a << n
(将a
的二进制位左移n
位)。右移运算符
>>
- 功能:将左侧操作数的二进制位向右移动指定的位数,左侧空出的位填充规则取决于数据类型:
- 对于无符号整数:左侧空出的位用
0
填充(逻辑右移)。- 对于有符号整数:左侧空出的位用原符号位填充(算术右移,保持正负性)。
- 语法:
a >> n
(将a
的二进制位右移n
位)2. I/O 流操作(仅 C++ 特有)
在 C++ 中,
<<
和>>
被重载为流运算符,用于标准库的输入输出(I/O)操作,这是 C 语言中没有的功能。
三 命名空间
1 C语言命名冲突
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{
// 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数”
printf("%d\n", rand);
return 0;
}
上述是一串C语言代码。 定义了一个全局变量rand, 但是如果包含了头文件 #include<stdlib.h>,就会和头文件的rand函数命名冲突
c语言项目类似上面程序这样的命名冲突是普遍存在的问题,C++引⼊namespace就是为了更好的解决这样的问题
2 namespace的定义
在不同的作用域,可以定义同一变量。 在同一作用域,不能定义同一变量。
• 定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
• namespace本质是定义出⼀个域,这个域跟全局域各⾃独⽴,不同的域可以定义同名变量,所以下⾯的rand不在冲突了。
• C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/
类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响
编译查找逻辑,还会影响变量的⽣命周期,命名空间域和类域不影响变量⽣命周期。
• namespace只能定义在全局,当然他还可以嵌套定义。
• 项⽬⼯程中多⽂件中定义的同名namespace会认为是⼀个namespace,不会冲突。
• C++标准库都放在⼀个叫std(standard)的命名空间中。
域作用限定符:: 作用---指定访问作用域
注意:域作用操作符前面要加上命名空间的名字,否则默认是全局,不会到命名空间搜索,找不到就是未声明的操作符
#include<stdio.h>
#include<stdlib.h>
namespace yq
//yq是命名空间的名字,这个名字是自定义的
{
//命名空间中可以定义函数,变量,类型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
int a = 1;
int main()
{
//编译器语法查找确定,默认规则是先局部查找后全局查找,如果局域中有所找变量,则不再遍历全局域,如果都没有扎到就报错未申明的标识符
int a = 0;
printf("%d", a);
//::域作用限定符
printf("%d", ::a);
//如果::的左边没有东西,则此时::a表示全局域中的a
printf("%p", rand);
//在头文件中的函数名,是一个指针,要用%p
printf("%d", yq::rand);
//在命名空间中的rand
// 编译器语法查找确认,指定作用域,就直接去这个域查找->没有找到就报错未声明的标识符
printf("%p\n",yq::Add);
//表示查找这个函数(&p)
printf("%d\n", yq::Add(1,2));
//调用函数
struct yq::Node node;
//注意yq::的位置 在Node之前,struct之后
return 0;
}
在查找时,如果没有指定要查找命名空间,则只会查找局部域和全局域。如果指定查找命名空间,则只会查找命名空间。
同一个文件用命名空间隔离相同命名不会冲突
全局域用命名空间隔离,相同命名不会冲突。
就像一个公司做项目,分成各种组,为了防止命名冲突,我们用命名空间隔离。
同一文件,如果命名空间名字相同,为了防止命名冲突,就只能改名字了。
公司里面同一个组不同的人也有可能用命名冲突,那怎么办?命名空间可以嵌套。
3 命名空间的嵌套
#include<stdio.h>
namespace ATM
{
//张三
namespace zs
{
int rand = 1;
int Add(int left, int right)
{
return left + right;
}
}
//李四
namespace ls
{
int rand = 2;
int Add(int left, int right)
{
return left + right;
}
}
}
int main()
{
printf("%d\n", ATM::zs::rand);
printf("%d\n", ATM::ls::rand);
return 0;
}
注意:命名空间可以一直嵌套,但是从实践的角度来说不要嵌套太多·
四 C++输入/输出
• <iostream>是Input Output Stream 的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输出对象。
• std::cin是istream类的对象,它主要⾯向窄字符(narrowcharacters(of type char))的标准输
⼊流。
• std::cout是ostream类的对象,它主要⾯向窄字符的标准输出流。
• std::endl是⼀个函数,流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区。
• <<是流插⼊运算符,>>是流提取运算符。(C语⾔还⽤这两个运算符做位运算左移/右移)
• 使⽤C++输⼊输出更⽅便,不需要像printf/scanf输⼊输出时那样,需要⼿动指定格式,C++的输⼊输出可以⾃动识别变量类型,其实最重要的是C++的流能更好的⽀持⾃定义类型对象的输⼊输出。
#include<iostream>
int main()
{
int i = 100;
i << 5;
//此时的<<运算符表示左移运算符 位运算不会改变变量本身的值,除非使用复合赋值运算符,例如i <<= 5;
double d = 1.1111;
// 任何变量,都转换成字符串,插入到流中
// 自动识别类型
std::cout << i <<'\n' << "\n";
//'\n'和"\n"都表示换行 相当于输出i
std::count << "Hello world" <<std::endl;
//std::endl也表示换行
std:count << i << " " << d << std::endl;
//输出 i 空格 d
std::cin >> i >> d;
//输入i和d的值
std::cout << i << " " << d << std::endl;
//输出前面输入的值
return 0;
}
上面代码的运行结果是:
100
Hello world
100 1.1111
123 12.3//此处为自己输入的值
123 12.3
如下所示,如果不想每一次都写std::count,可以在头文件后面写 using namespace std;代表全局展开。
但是使用这种方法弊端很大:展开命名空间中全部成员,项目不推荐,冲突风险很大,日常小练习程序为了方便推荐使用。
#include<iostream>
using namespace std;
int main()
{
int i = 100;
double d = 1.1111;
cout << "hello world" << endl; // end line
cout << i << " " << d << endl;
return 0;
}
还可以使用部分展开
#include<iostream>
#include<algorithm>
// 部分展开
using std::cout;
using std::endl;
int main()
{
int i = 100;
double d = 1.1111;
cout << "hello world" << endl; // end line
cout << i << " " << d << endl;
cout << i << " " << d << endl;
cout << i << " " << d << endl;
std::cin >> i;
int a[] = { 4,1,5,7 };
std::sort(a, a + 4);
return 0;
}
其他:
#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;
}
五 缺省参数
• 缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调用该函数时,如果没有指定实参则采⽤该形参的缺省值,否则使⽤指定的实参,缺省参数分为全缺省和半缺省参数。(有些地方把缺省参数也叫默认参数)
• 全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。
• 带缺省参数的函数调用,C++规定必须从左到右依次给实参,不能跳跃给实参。
• 函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省
值。
1 缺省参数的调用
#include <iostream>
#include <assert.h>
using namespace std;
void Func(int a = 0)
{
cout << a << endl;
}
int main()
{
Func(); // 没有传参时,使⽤参数的默认值 运行结果是0
Func(10); // 传参时,使⽤指定的实参 运行结果是10
return 0;
}
2 全缺省和半缺省
#include<iostream>
using namespace std;
//全缺省
void Funcl(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
//半缺省
void Func2(inta, int b=10, int c = 20)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
int main()
{
Func1();
Func1(1);
Func1(1,2);
Func1(1,2,3);
Func2(100);
Func2(100, 200);
Func2(100, 200, 300);
return 0;
}
上述的运行结果:
//全缺省
a=10;
b=20;
c=30;
a=1;
b=20;
c=30;
a=1;
b=2;
c=30;
a=1;
b=2;
c=3;
//半缺省
a=100;
b=10;
c-20;
a=100;
b=200;
c=20;
a=100;
b=200;
c=300;
3 缺省参数不能申明和定义同时给
// Stack.h
void STInit(ST* ps, int n = 4);
// Stack.cpp
#include"Stack.h"
// 缺省参数不能声明和定义同时给
void STInit(ST* ps, int n)//上面已经给过缺省参数,所以此处不能给
{
assert(ps && n > 0);
ps->a = (STDataType*)malloc(n * sizeof(STDataType));
ps->top = 0;
ps->capacity = n;
}
4 缺省参数的使用示例
如果·在一个数组中查找出所有的一个数,可以利用到缺省参数
//stack.h
SLFind(&s2, 4,int i=0)
//test.h
// 5 4 6 3 4 7 4
// 查找出所有的4
int i = SLFind(&s2, 4);
//下一次从i+1的位置开始查找
while (i != -1)
{
i = SLFind(&s2, 4, i+1);
}
return 0;
}
5 缺省函数不能传空
传值是连续传的,不能跳过传下一个,以传三个为例:要么传第一个、要么传前两个,要么不传。
比如我们main函数就传3个值,如下,不能这样传
Func(1, ,3)
不能传空(这个不会用缺省值补,如果非要用某个默认值要么你就把参数位置调换一下)。
缺省值是从左往右给的,实参传参是从右往左传的。
六 函数重载
C++支持在同⼀作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调用就表现出了多态行为,使用更灵活。C语言不支持同⼀作用域中出现同名函数的。
在C++中使用同名函数的好处:如果有n个函数实现方法相同,但是参数类型,个数,顺序不同等,若在C语言中,调用函数时需取不同的名字且每次调用时都要查找名字十分的不方便。但是在C++中可以使用同名函数,C++会自动识别函数的类型不同等。
1 参数类型不同
#include<iostream>
using namespace std;
// 1、参数类型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
int main()
{
//C++会自动识别参数的类型
Add(10, 20);
Add(10.1, 20.2);
}
2 参数个数不同
// 2、参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
int main()
{
f();//不传参数调用第一个
f(1);//传参数调用第二个
}
3 参数类型顺序不同
// 3、参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
int main()
{
f(10,'a');
f('a',10);
}
4 不能构成的情况
(1)返回值不同不能作为重载条件,因为调用时无法区分
void f1()
{}
int f1()
{
return 0;
}
(2)下面两个函数构成重载,但是f()在调用时,会报错,存在歧义,编译器不知道调用谁
void f1()
{
cout << "f()" << endl;
}
void f1(int a = 10)
{
cout << "f(int a)" << endl;
}
int main()
{
f1(1);//传参调用时没有问题
f1();//编译器报错:对重载函数的调用不明确,不传参在语法上两个函数都可以调用
七 C++的一些参考文件
https://legacy.cplusplus.com/reference/
http:// https://zh.cppreference.com/w/cpp