学习目标:C++的一些高级操作
根据C++菜鸟教程自学的笔记,大家有想学习C++的话可以根据这个网站进行学习。这个推荐有一定基础的再去进行自学。新手的话还是建议直接看一些视频跟着学
学习内容:
1. 运算符重载
说到C++中的运算符重载,首先要明确平时使用的加、减、乘、除、与、或、非、输入、输出、赋值、自增自减等这些运算符其实就是运算符重载的特殊情况。这些特殊情况只能对C++中的基础数据类型进行操作,如:“int”、“double”等。 而如果是用户自己定义的类型呢?比如自己定义的类的对象进行运算,想让这些自定义类型也进行加、减、乘、除这些操作,就只能进行运算符重载。
(1).重载运算符的定义
Box是声明的一个类:
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
2.文件读取和写入
定义数据类型:#include
写文件:
ofstream outfile;
outfile.open("file.dat", ios::out | ios::trunc );
关闭文件outfile.close();
读文件
ifstream afile;
afile.open("file.dat", ios::out | ios::in );
关闭文件infile.close();
3. 常量
const关键字的定义:
const int A = 1;
此时a为常量,是不可以修改的
#define 预处理器
#define PI 3.1415926
宏定义的边际错误
#include <iostream>
using namespace std;
#define cyy 10+1
int main()
{
int area;
const int a = 1;
area = cyy * cyy;
cout << area<<endl;//
/
cout << cyy;//cyy的值为11
return 0;
}
理想情况上面的代码输出的area值,应该为1111=121,但结果却为21
原因是宏定义的编辑效应产生的错误,代码将area = cyycyy看成了area = 10+1*10+1
在定义常量或宏定义时,应用大写字母表示常量,这是很好的编程方式
4. 变量的作用域
全局变量:所有函数和代码块之外声明的变量,可以被任何代码中的函数访问,程序开始时创建,程序 结束时销毁。
局部变量:局部变量是在函数内部定义的量
块变量:块变量是在if,while中定义的
使用外部链接的函数或全局变量,使用extern函数
extern关键字,是在多个文件使用同一个变量时的声明方式
#include <iostream>
int a = 1;
void func()
{
int a = 10;
{
int a = 20; // 块作用域变量
std::cout << "块变量: " << a << std::endl;
}
std::cout << "局部变量: " << a << std::endl;
}
int main() {
func();
std::cout << "全局变量: " << a << std::endl;
return 0;
}
输出结果:
块变量: 20
局部变量: 10
全局变量: 1
5.存储类的关键字
存储类主要的作用是在定义变量或函数的范围和生命周期。c++的程序中的存储类有:
(1)register
用于定义存储在寄存器而不是RAM中的变量,以便更快访问
(2)static
- 限制变量的作用域在当前文件中,该变量只能在定义他的文件中使用
- static声明完静态变量后,在程序的整个执行期间都会一直存在,即使超出的声明他的函数的作用域
(3) extern
用于声明其他文件中定义的全局变量,也就是在B文件中使用A文件声明的全局变量
6.字符串的使用
C++中,有很多用于操作字符串的函数,这里记录一下
在使用这些函数前,需声明一下:
#include <string>
函数 | 作用 |
---|---|
strcpy(s1,s2) | 复制字符串S2到S1中 |
strcat(s1,s2) | 将S2链接到S1的末尾 |
strlen(s1) | 返回字符串s1的长度 |
strcmp(s1,s2) | 判断字符串s1和s2是否相同,相同返回0,s1<s2返回值小于0,s1>s2返回值大于0 |
strchr(s1,ch) | 返回一个指针,指向ch第一次出现在字符串s1的位置 |
strstr(s1,s2) | 返回一个指针,指向字符串s1中,第一次出现字符串s2的位置 |
7. 引用
我在之前的学习引用的时候有一处一直搞不用,就是引用作为函数的参数,今天问了chatgpt一个问题后,豁然开朗
问题:void swap(int& x, int& y);和void swap(int x, int y)的区别
回答:
- 参数传递方式:第一个函数 void swap(int& x, int& y); 使用引用作为参数,而第二个函数 void swap(int x, int y); 使用值传递。
- 修改原变量:当调用 void swap(int& x, int& y); 函数时,函数内部可以直接修改传入的变量 x 和 y 的值。因为引用传递的是变量的地址,所以修改引用的值会影响到原变量。而当调用 void swap(int x, int y); 函数时,函数内部只能修改传入的副本变量 x 和 y 的值,不会影响原变量。
- 性能开销:使用引用传递可以避免对变量进行拷贝操作,提高代码的执行效率。而使用值传递需要进行值的拷贝,可能会产生较大的开销。
代码案例
- 函数使用引用
#include <iostream>
using namespace std;
int x = 10;
int y = 20;
void swap(int &x, int& y) {
int temp = x;
x = y;
y = temp;
}
int main() {
cout << "x:" << x << endl;
cout << "y:" << y << endl;
swap(x, y);
cout << "new_x:" << x << endl;
cout << "new_y:" << y << endl;
}
输出结果://x和y的值做了交换
x:10
y:20
new_x:20
new_y:10
- 函数不使用引用
#include <iostream>
using namespace std;
int x = 10;
int y = 20;
void swap(int x, int y) {
int temp = x;
x = y;
y = temp;
}
int main() {
cout << "x:" << x << endl;
cout << "y:" << y << endl;
swap(x, y);
cout << "new_x:" << x << endl;
cout << "new_y:" << y << endl;
}
输出结果://x和y的值没做交换
x:10
y:20
new_x:10
new_y:20
总结:也就是说,当你想值使用传入变量的值时,使用值传递即可。但是当你想要修改传入变量的值时,使用引用传递,同时如果传入的参数过大,避免拷贝占用内存,使用引用传递也是很好的。
8.结构
C++中用户可以自定义可用的数据类型,这个叫结构体。
定义结构,使用的是struct语句,其结构如下:
struct Books{
char title[50];
char author[50];
char subject[100];
int book_id;
} book;
可以对结构体进行声明和赋值
Books Book1
strcpy(Book1.title,"C++教程");
Book1.bool_id = 1;
9.抽象类和纯虚函数
纯虚函数和抽象类之间是紧密关联的,抽象类是包含至少一个纯虚函数的类。纯虚函数没有具体的实现,只是提供了函数的接口。抽象类不能被实例化,只能用作其他类的基类来派生新的类。
抽象类是一种特殊的类,不能被实例化,实例化抽象类将会报错。
抽象类的定义:定义抽象类,需要抽象类中至少要有一个纯虚函数
纯虚函数的定义: 纯虚函数通过在函数末尾使用=0来定义 virtual void pureVirtualFunction() = 0;
一个纯虚函数的和抽象类的使用方法
class Base {
public:
virtual void pureVirtualFunction() = 0; // 纯虚函数声明
void normalFunction() {
// 普通函数实现
// ...
}
};
class Derived : public Base {
public:
void pureVirtualFunction() override {
// 派生类中重写纯虚函数,提供具体实现
// ...
}
};
10. 虚函数和纯虚函数的区别
虚函数 | 纯虚函数 | |
---|---|---|
声明 | 函数前要加virtual关键字 | 使用virtual 和“=0”声明 |
派生类中是否需要重写 | 随意 | 必须重写 |
11. 命名空间
当你自定义的函数如zzy(),与另外一个函数库也有一个zzy()函数,编译器就无法判断你使用的时哪一个zzy()函数
引入命名空间专门为了解决这个问题
定义命名空间
命名空间是需要定义的,这个目前我没有设计到,但其实就时把对应的函数放到命名空间这个大括号中即可
namespace namespace_name{
void func(){
具体函数实现
}
}
命名空间的使用
using namespace std; 这个是最常见的命名空间
如果不使用这个命令的话,很对c++的内置函数使用时就需要用“std::函数”的形式来使用
12.模板
13.lambda函数
lambda函数是在C++ 11中新增的功能,广泛定义于需要定义短小的函数对象、算法和容器操作
lambda的定义
[capture](parameters) -> return_type { body }
auto sum = [](int a, int b) -> int {
return a + b;
};
capture 是一个可选的捕获列表,用于在lambda函数内部访问外部变量。
parameters 是参数列表,用于传递给lambda函数的参数。
return_type 是返回类型,指定lambda函数的返回值类型。
body 是函数体,包含lambda函数的具体实现逻辑。