文章目录
1C++概述
1.1初识C++
发展历程:
C++ 由本贾尼・斯特劳斯特卢普在 20 世纪 70 年代开发,它在 C 语言的基础上增加了面向对象编程的特性,最初被称为 “C with Classes”,后来逐渐发展成为独立的 C++ 语言。
语言特点
(1)高效性:C++ 允许直接操作计算机硬件,能够对内存进行精细的管理,具有很高的执行效率,适用于开发对性能要求极高的程序,如游戏、操作系统、嵌入式系统等。
(2)面向对象:支持面向对象编程,将数据和操作数据的方法封装在类中,通过继承和多态性实现代码的复用和扩展,使程序结构更加清晰、易于维护。
(3)兼容性:与 C 语言保持了高度的兼容性,使得 C++ 可以直接使用大量现有的 C 语言代码和库,同时还能享受到 C++ 面向对象和高级特性带来的便利。
(4)丰富的库:拥有标准模板库(STL)等丰富的库,提供了各种常用的数据结构(如向量、链表、映射等)和算法(如排序、搜索等),大大提高了开发效率。
应用领域
C++ 应用广泛,除了上述提到的游戏开发、操作系统和嵌入式系统外,还用于图形图像、数据库管理、网络编程、金融计算等领域。
C和C++的关系
C++ 是 C 语言的超集,它继承了 C 语言的优点,同时又进行了扩展和改进。
g++编译器的下载
g++编译器是用来编译C++的;
sudo apt update
sudo apt install g++
第一个C++程序
#include<iostream> // 包含输入输出流库,使得程序可以使用标准输入输出功能,如 cout 和 cin
using namespace std; // 使用标准命名空间,这样在使用标准库中的对象和函数时就不需要加 std:: 前缀
int main(int argc, char* argv[]) // 主函数,程序的入口点,argc 表示命令行参数的数量,argv 是一个指向命令行参数的字符指针数组
{
cout << "hello world" << endl; // 输出字符串 "hello world" 到标准输出(通常是控制台),endl 表示换行
return 0; // 主函数返回 0,表示程序正常结束
}
2.命名空间
2.1命名空间的概述
1.什么是实体?
变量、常量、函数、结构体、引用、类、对象、模板、命名空间等,它们都称为实体
2.什么是命名空间?
命名空间又称为名字空间,是程序员命名的内存区域,程序员根据需要指定一些有名字的空间域,把一些全局实体分别存放到各个命名空间中,从而与其他全局实体分隔开
比如c++中的输入输出cin和cout就在std标准命名空间中定义;
3.为什么要使用命名空间?
大型工程常由多人分工完成,不同人设计不同头文件,这可能导致不同头文件中类或函数命名相同,引发名字冲突。C++为解决该问题引入命名空间,它是用户自定义的作用域,在不同作用域可定义同名变量且互不干扰,系统能进行区分。
4.命名空间的基本格式
namespace 名
namespace A
{
int a=10;
......
}
2.1命名空间基本使用
1.作用域限定符::
每次要使用某个命名空间中的实体时,都直接加上作用域限定符::
#include<iostream> // 引入输入输出流库,用于使用 cout 和 endl 进行输出操作
// 定义命名空间 A
namespace A {
int a = 10; // 在命名空间 A 中定义一个整型变量 a 并初始化为 10
void fun1(void) { // 在命名空间 A 中定义一个无返回值、无参数的函数 fun1
std::cout << "a=" << a << std::endl; // 输出命名空间 A 中变量 a 的值,并换行
}
}
// 定义一个无返回值、无参数的函数 test1
void test1(void) {
A::fun1(); // 调用命名空间 A 中的 fun1 函数,输出 a 的初始值
A::a = 100; // 修改命名空间 A 中变量 a 的值为 100
A::fun1(); // 再次调用命名空间 A 中的 fun1 函数,输出修改后 a 的值
}
// 主函数,程序的入口点
int main(void) {
test1(); // 调用 test1 函数
return 0; // 主函数正常结束,返回 0
}
好处:准确,只要命名空间中确实有这个实体,就能够准确调用(访问)
坏处:繁琐
2.using编译指令
如第一个C++程序,其中std代表的是标准命名空间。cout和endl都是std中的实体,使用了using编译指令后,这两个实体就可以直接使用了。(把命名空间中的所有实体一次性引入到程序)
#include<iostream>
using namespace std;
namespace A
{
int a=100;
void fun1(void)
{
cout<<"a="<<a<<endl;
}
}
using namespace A;
void test1(void)
{
fun1();
a=200;
fun1();
}
int main(void)
{
test1();
return 0;
}
~
~
建议1
using编译指令尽量写在局部作用域,这样using编译指令的效果也会在其作用域结束时结束;可以避免命名冲突
错误案例
#include<iostream>
using namespace std;
namespace A
{
int a=100;
}
namespace B
{
int a=200;
}
using namespace A;
using namespace B; //A和B命令空间里的变量a会产生冲突
void test1(void)
{
//这里的a不知道访问A还是B里的变量a;
a=200;
cout<<"a="<<a<<endl;
}
int main(void)
{
test1();
return 0;
}
解决办法
1.使用作用域限定符
2.using编译指令尽量写在局部作用域,避免和其他变量名起冲突
3.using声明机制(推荐)
—— 需要什么就声明什么
建议将using声明语句写在局部作用域中。此时即使命名空间中实体与全局位置实体重名,在局部位置也遵循“就近原则”形成屏蔽
#include<iostream>
using std::cin;
using std::cout;
using std::endl;
namespace A
{
int a=10;
int b=20;
}
int a=1000;
void test1(void)
{
using A::a;
cout<<"a="<<a<<endl;
}
int main(int argc,char*argv[])
{
test1();
cout<<"a="<<a<<endl;
return 0;
}
4.命名空间的嵌套使用
命名空间中还可以定义命名空间
#include<iostream>
using std::cin;
using std::cout;
using std::endl;
namespace A
{
int a=10;
int b=20;
namespace B
{
int c=200;
}
}
void test1(void)
{
using A::B::c;
cout<<"c="<<c<<endl;
}
int main(int argc,char*argv[])
{
test1();
return 0;
}
5.匿名命名空间
命名空间还可以不定义名字,不定义名字的命名空间称为匿名命名空间(简称匿名空间)。
通常,如果我们希望一部分实体只在本文件中起作用,那么可以将它们定义在匿名空间中。
namespace {
//...
}
使用匿名空间中实体时,可以直接使用,也可以加上作用域限定符(没有空间名)
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
namespace
{
int a=100;
int b=200;
int c=300;
}
int main(int argc,char*argv[])
{
cout<<"a="<<a<<endl;
int b=2;
cout<<"b="<<b<<endl;
cout<<"b="<<::b<<endl;
return 0;
}
~
~
匿名空间注意事项:
(1)匿名空间不要定义与全局空间中同名的实体;
(2)匿名空间中的实体不能跨模块调用。
6.命名空间补充
在同一个源文件中可以多次定义同名的命名空间,被认为是同一个命名空间,所以不能在其中定义相同的实体
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
namespace A
{
int a=100;
}
namespace A
{
int c=200;
}
void test(void)
{
using A::a;
using A::c;
cout<<"a="<<a<<endl;
cout<<"c="<<c<<endl;
}
int main(int argc,char*argv[])
{
test();
return 0;
}
3.const关键字
3.1修饰内置类型
const修饰的变量称为const常量,之后不能修改其值。(本质还是变量,使用时也是当成变量使用,只是被赋予只读属性)
const常量在定义时必须初始化。
#include<iostream>
using std::cin;
using std::cout;
using std::endl;
void test1(void)
{
//只有读权限,没有写权限
const int number1 = 10;
int const number2 = 20;
cout<<number1<<endl;
cout<<number2<<endl;
}
int main(void)
{
test1();
return 0;
}
宏定义的方式创建常量
#define MAX 100
3.2修饰指针类型*
1. 指向常量的指针
指向常量的指针意味着不能通过该指针来修改所指向对象的值,不过指针本身可以指向其他对象。
const 数据类型 * 指针名;
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main(void) {
int num = 10;
const int *ptr = #
// *ptr = 20; // 错误,不能通过指向常量的指针修改所指向的值
num = 20; // 可以直接修改 num 的值
cout << *ptr << endl; // 输出 20
return 0;
}
2. 常量指针
常量指针表示指针本身的值(即所指向的地址)不能被修改,不过可以通过该指针修改所指向对象的值。
数据类型 * const 指针名;
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main(void) {
int num1 = 10;
int num2 = 20;
int * const ptr = &num1;
*ptr = 30; // 可以通过常量指针修改所指向的值
// ptr = &num2; // 错误,不能修改常量指针的值
cout << *ptr << endl; // 输出 30
return 0;
}
3. 指向常量的常量指针
指向常量的常量指针既不能修改指针所指向对象的值,也不能修改指针本身的值。
const 数据类型 * const 指针名;
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main(void) {
int num1 = 10;
int num2 = 20;
const int * const ptr = &num1;
// *ptr = 30; // 错误,不能通过指向常量的常量指针修改所指向的值
// ptr = &num2; // 错误,不能修改指向常量的常量指针的值
cout << *ptr << endl; // 输出 10
return 0;
}
总结
指向常量的指针:不能通过指针修改所指向对象的值,但指针可以指向其他对象。
常量指针:指针本身的值不能修改,但可以通过指针修改所指向对象的值。
指向常量的常量指针:既不能修改指针所指向对象的值,也不能修改指针本身的值。
3.3const 常量和宏定义常量区别
发生时机:
宏定义在 C 语言预处理阶段,单纯进行字符串替换。比如 #define PI 3.14 ,预处理时会把代码中所有 PI 都替换成 3.14 。
const 常量在编译时起作用,它本质近似变量,只是被 const 限定为只读。像 const double pi = 3.14; ,编译时会处理这个定义。
类型和安全检查:
宏定义没有类型概念,不做类型检查。如 #define ADD(a, b) ((a)+(b)) ,传入不同类型参数都直接替换展开,可能引发错误。
const 常量有明确类型,编译期会执行类型检查。如 const int num = 5; ,类型不匹配的赋值等操作编译时会报错 。
通常推荐用 const 常量替代宏定义常量,能利用编译期类型检查机制,减少因宏定义的简单替换带来的错误风险。