首先你要去网上下载对应的运行软件,先把对应的 C++
环境配置好,配置好了我们就可以开始我们的C++
学习之旅了。希望通过学习我们能够成为一个比较不错的 C++
开发工程师。我也会持续更新 C++
知识。
1. C++语法基础
当我通过 CLion
工具创建了一个新的 Project
。
示例:
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
问题:
-
这一来我就不理解了
std::cout << "Hello, World!" << std::endl;
这一句语法是什么意思?std::cout
是一个预定义的对象,这个对象是std::iostream
类的一个实例。count
实际上全名为character output
的缩写。当你使用std::cout
的时候,就是在使用一个输出流对象,它连接到输出设备,通常是你的计算机屏幕。接着它会插入运算符(
<<
)时,会将紧跟在<<
后面的表达式或字符串插入到输出流中。例如,在std::cout << "Hello, World!";
中,字符串"Hello, World!"
被插入到std::cout
对象中,然后输出到屏幕。std::endl
也是<iostream>
库中的一个预定义标识符,它代表end line
,意味着结束当前行并插入一个换行符。当你在输出流中使用std::endl
时,它不仅会插入一个换行符,还会刷新输出缓冲区,确保所有待输出的数据都被立即写入到标准输出设备上。刷新缓冲区是必要的,因为默认情况下,
C++
可能不会立即显示所有输出。相反,它可能会在内部缓冲输出,直到缓冲区满或遇到某些特定条件才真正写入屏幕。std::endl
确保了即时显示,这对于调试和实时输出特别有用。 -
为什么要返回一个
0
呢?可不可以用void main(){}
?返回类型:之所以返回类型是
int
。这是因为操作系统期望main()
函数返回一个整数值,这个值用于指示程序的退出状态。通常,返回0
表示程序成功执行,非零值则表示程序执行过程中遇到了错误或异常情况。返回0的意义: 当
main()
函数返回0时,它告诉操作系统(或者父进程)程序已经正常终止,没有遇到任何错误。这是一个约定俗成的做法,虽然不同的系统对这个值的具体含义可能有所不同,但在大多数情况下,0被视为成功的标志。为什么不能用
void main()
: 使用void main()
是不正确的,因为这违反了C++标准。C++标准规定main()
函数必须返回一个整数,这主要是为了便于操作系统或父进程判断子进程的执行结果。如果你在某些编译器上看到void main()
能够编译通过,那是因为编译器可能提供了非标准的扩展,允许这种用法,但这并不符合C++标准,也不可移植。在严格遵循标准的编译器或环境下,void main()
将会导致编译错误。argc
和argv
参数: 虽然在最简单的程序中,main()
可能只被定义为int main()
, 更常见的形式是int main(int argc, char *argv[])
或int main(int argc, char **argv)
. 这两个参数允许程序接收来自命令行的参数。argc
表示参数的数量(包括程序名称本身),argv
是一个指向字符串数组的指针,其中每个字符串都是一个命令行参数。
变量和数据类型
不同的数据类型:
int
, float
, double
, char
, bool
, string
等。
/**
* 变量和数据类型
* @return
*/
int main() {
int age = 25;
float height = 1.82f;
double pi = 3.14159265358979323846;
char grade = 'A';
bool isStudent = true;
std::string name = "John Doe";
std::cout << name << std::endl;
std::cout << "Name: " << name << ", Age: " << age << std::endl;
std::cout << "height: " << height << ", pi: " << pi << std::endl;
std::cout << "grade: " << grade << ", isStudent: " << isStudent << std::endl;
return 0;
}
输出:
这里面需要注意的是 string
的变量的运用。
声明和初始化变量:
其实初始化变量也是很简单的,对于变量来说无非就是全局变量和局部变量。
全局变量一般都定义在外部,初始化的话直接赋值即可。
局部变量一般定义在函数内部,初始化也很简单,直接赋值即可。
举例:
#include <iostream>
int globalVar = 10; // 全局变量的定义和初始化
int main(){
int localVar = 5; // 局部变量的定义和初始化
std::cout << "全局变量:" << globalVar << std::endl; // 输出:全局变量:10
std::cout << "局部变量:" << localVar << std::endl; // 输出:局部变量:5
return 0;
}
变量的作用域和生命周期:
决定了变量在哪里可被访问,而生命周期则确定了变量存在的时间。
- 局部变量:在函数内声明的变量,作用域仅限于该函数,生命周期在函数执行完毕后结束。
- 全局变量:在所有函数外声明的变量,作用域贯穿整个程序,生命周期从程序开始到程序结束。
- 静态局部变量:在函数内声明但具有
static
关键字的变量,作用域限于函数,但生命周期是整个程序。
举例:
#include <iostream>
void scopeLifecycle (int& age){
static int number = 0; // 静态局部变量
number++;
age++;
std::cout << "静态局部变量:" << number << std::endl;
std::cout << "函数的实参:" << age << std::endl;
}
int main(){
int localVar = 10;
scopeLifecycle(localVar);
scopeLifecycle(localVar);
std::cout << "外部函数变量: " << localVar << std::endl;
return 0;
}
输出结果:
通过这里我们就可以看见对于静态局部变量来说,它的生命周期是整个程序,如果不是的话,它的值每一次都应该是 1
,而不是 2
。
外部变量因为传入的时候,其实是将引用的地址传给了形参,所以导致函数内部修改时,是修改的源数据,如果不加 &
的话,我们是无法修改源数据的。最后应该打印:外部函数变量:10
控制结构
掌握条件语句:
if
, else if
, else
对于条件语句来说,跟我们学过的语言就是大相径庭的。直接上代码吧。
举例:
#include <iostream>
int main() {
int age;
std::cout << "请输入心理年龄: ";
std::cin >> age;
if (age >= 18) {
std::cout << "欢迎来到大人的世界!" << std::endl;
} else if (age < 18 && age >= 0) {
std::cout << "你还是个宝宝!" << std::endl;
} else {
std::cout << "你在开玩笑!" << std::endl;
}
return 0;
}
输出结果:
循环结构:
for
, while
, do...while
,对于循环结构的学习其实也是跟其它语言类似的,直接上代码吧。
举例:
#include <vector>
#include <iostream>
int main() {
int age;
// 创建一个空的 vector,可以存储 int 类型的数据(动态列表)
std::vector<int> numbers;
do {
std::cout << "请输入心理年龄(小于等于0时暂停输入): ";
std::cin >> age;
if (age >= 18) {
std::cout << "欢迎来到大人的世界!" << std::endl;
} else if (age < 18 && age >= 0) {
std::cout << "你还是个宝宝!" << std::endl;
} else {
std::cout << "你在开玩笑!" << std::endl;
}
numbers.push_back(age);
} while (age > 0);
for (int i = 0; i < numbers.size() - 1; ++i) {
std::cout << "您已输入年龄(使用for循环):" << numbers[i] << std::endl;
}
int i = 0;
while (i < numbers.size() - 1) {
std::cout << "您已输入年龄(使用while循环):" << numbers[i] << std::endl;
i++;
}
}
输出结果:
ser-images%5Cimage-20240816091210276.png&pos_id=img-wN9q5STQ-1724900436681)
switch
语句和break
, continue
控制语句:
switch:
int main(){
int number;
std::cout << "请输入校验的数字: ";
std::cin >> number;
switch (number) {
case 1:
std::cout << "数字为1" << std::endl;
break;
case 2:
std::cout << "数字为2" << std::endl;
break;
case 3:
std::cout << "数字为3" << std::endl;
break;
default:
std::cout << "数字不是 1, 2, 3" << std::endl;
}
}
输出结果:
整体来说这个 switch
的用法也是很简单,但是这里需要注意的一点是,我们需要在每一种 case
的情况之下加入break
,要不然它会在匹配完成之后,继续执行下面 case
的代码。
输出结果:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
continue:
int main() {
for (int i = 0; i < 5; ++i) {
if (i == 2) {
continue;
}
std::cout << "i = " << i << std::endl;
}
return 0;
}
输出结果:
函数
定义和调用函数:
函数的定义:
返回类型 函数名(参数类型 参数名, ...) {
// 函数体
...
return 表达式;
}
调用:
函数名(参数值, ...);
参数传递方式:值传递、引用传递:
- 值传递:将实参的值复制给形参,这样在函数内部对参数所做的修改不会影响到实参。
- 引用传递:通过引用传递参数,实际上是将实参的地址传递给形参,这样在函数内部对参数的修改会影响到实参。
void swapByValue(int a, int b) { // 值传递
int temp = a;
a = b;
b = temp;
}
void swapByReference(int& a, int& b) { // 引用传递
int temp = a;
a = b;
b = temp;
}
返回值和函数重载:
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
数组
创建和初始化数组:
数组在创建时就需要确定其大小,这是因为数组的大小是在编译时期就必须固定的。其它的使用上面来说,还是比较简单的,所以直接上代码。
-
显式创建数组:
int arr[] = {1, 2, 3, 4, 5}; // 初始化数组(显式给出初始值)
-
隐式创建数组:
int arr[5] = {1, 2, 3}; // 未初始化的元素会被自动初始化为0
访问和操作数组元素:
// 访问和操作数组元素
int main(){
int arr[] = {10, 20, 30, 40, 50};
arr[0] = 100; // 修改第一个元素
int firstElement = arr[0]; // 获取第一个元素
std::cout << "打印数组第一个元素:" << firstElement << std::endl;
return 0;
}
多维数组的使用:
多维数组可以表示表格或多维数据集。例如,二维数组可以表示矩阵:
// 多维数组的使用
int main() {
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 访问元素
int element = matrix[1][2]; // 获取第二行第三列的元素
std::cout << "打印矩阵中第二行第三列元素:" << element << std::endl;
return 0;
}
2. 指针和引用
指针
在 C++
中指针的使用和 C
语言里面都是大相径庭的,都是用 *
和 &
配合使用。
指针是一种变量,它存储的是另一个变量的地址。在 C++
中,使用星号*
来声明指针。
直接上代码:
int main() {
using std::cout;
using std::endl;
int *p; // 声明一个指向整数的指针
// 给指针赋值一个变量的地址,可以使用取地址运算符 &
int num = 10;
p = # // p现在指向num的地址
cout << "变量 p 存储的内容:" << p << endl;
// 使用指针时,可以通过解引用运算符*来访问它所指向的变量的值
cout << *p << endl;// 输出num的值,即10
return 0;
}
指针算术和指针作为函数参数:
指针也是可以作为形参,但是如果直接 param+1
的话表示增加指针所指向的地址,使其指向下一个整数位置,而不是简单地增加一个字节。我们需要修改它的值的话还是需要通过 *param
去修改。
void increment(int *param){
(*param)++;
}
int main(){
int x = 5;
increment(&x);
std::cout << x; // 输出6
return 0;
}
动态内存分配:new
和 delete
:
new
和 delete
是C++中用于动态内存分配的关键字。new
用于在运行时分配内存,而 delete
用于释放不再需要的内存,以避免内存泄漏。
int *ptr = new int(10); // 分配一个整数内存并初始化为10
*ptr = 20; // 更改值
delete ptr; // 释放内存
空指针和野指针的概念:
- 空指针:顾名思义就是指针指向了无效的内存地址,通常这一块内存地址为
NULL
。这样会导致使用这个指针的时候出现未定义行为。 - 野指针:指向已释放或未分配内存的指针。访问野指针也会导致未定义行为,可能是程序崩溃或其他不可预测的结果。
引用
引用的声明和初始化:
引用是干什么的呢?其实它相当于一个别名,并且在其生命周期内不能重新指向其他变量。引用的声明使用 &
符号,但与取地址运算符不同:
int &ref = num; // ref现在是num的一个别名
引用作为函数参数与返回值:
引用经常用作函数参数,以避免复制大对象的成本,同时允许函数修改传入的对象:
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
引用也可以作为函数的返回值,但要注意不要返回局部变量的引用,因为局部变量在函数结束时会被销毁。
引用与指针的区别:
- 引用在初始化之后是不能更改的,但是指针可以。
- 引用必须在声明时初始化,而指针可以在任何时候初始化。
- 引用是不可能为空状态的,但是指针有可能是空的。
- 引用更安全,因为它们不会导致悬空指针的问题。
3. 面向对象编程(OOP
)
面向对象编程就是一种编程范式,它是通过抽象出来 ”对象“ 来设计软件。在 C++ 中,面向对象编程的主要特性包括类和对象、封装、继承和多态。
类和对象
类:只是用户定义的一种数据类型,它内部封装了属性和方法。
对象:是类的实例,每一个对象都有属于自己的属性和方法。
#include <iostream>
class Circle {
private:
float radius;
public:
float getArea() const {
return 3.1415926 * radius * radius;
}
void setRadius(float r) {
radius = r;
}
float getRadius() {
return radius;
}
};
int main(){
Circle circle;
circle.setRadius(5);
std::cout << "圆的半径为" << circle.getRadius() << " 面积为:" << circle.getArea() << std::endl;
return 0;
}
这里我想提一下上面的细节:
上面在我们的 getArea
函数中,我们的形参后面还有 const
这样一个关键字,那它有什么用呢?
-
const
关键字用于声明一个变量或者表明一个函数不会修改类的成员变量。 -
在成员函数的上下文中,
const
关键字放在函数定义的末尾,紧跟着参数列表,意味着这个成员函数是一个常量成员函数,它保证不修改任何类的非静态数据成员。
对于getArea()
这样的成员函数,其主要任务是计算并返回圆的面积,而不需要修改Circle
类的任何状态。
const
成员函数的好处有什么呢?
- 提高代码的安全性:它明确地表示了函数不会修改对象的状态,这有助于防止意外修改数据。
- 允许对常量对象的调用:如果一个
Circle
对象被声明为const
,那么只有它的const
成员函数可以被调用。如果没有将getArea()
声明为const
,那么尝试在一个常量对象上调用它将导致编译错误。 - 优化编译器可能的优化:由于编译器知道
const
成员函数不会改变对象状态,它可以做出更安全的优化。
构造函数和析构函数:
构造函数的作用就是用于初始化对象。
析构函数的作用就是在对象生命周期结束时调用,用于清理资源。
class Circle {
public:
Circle() { std::cout << "Circle 对象构建成功!" << std::endl; }
~Circle() { std::cout << "Circle 对象销毁成功!" << std::endl; }
};
封装
公有(public
)、私有(private
)和保护(protected
)访问修饰符:
不同的访问修饰符控制了成员的访问级别。
public
:- 公有成员对任何代码都是可见和可访问的,无论这些代码是在同一个文件中,还是在另一个文件或另一个类中。
- 这是默认的继承模式,也就是说,当一个类从另一个类派生时,基类的公有成员在派生类中保持公有。
private
:- 私有成员只能被类的成员函数访问,不能从类的外部直接访问。
- 私有成员对于封装非常重要,因为它们隐藏了类的实现细节,使得类的使用者不必关心这些细节。
- 在继承中,基类的私有成员不能被派生类访问,也不能被派生类的成员函数访问。
protected
:- 保护成员在类的内部和派生类中都是可见和可访问的,但在类的外部不可见。
- 这意味着派生类可以访问其基类的保护成员,但类的外部代码不可以。
- 保护成员在实现继承关系时很有用,因为它们允许派生类访问基类的某些实现细节,同时又不让这些细节暴露给最终用户。
使用getter
和setter
方法:
这个在上面的 Circle
类中也是有的。 一般都是在 pubic
中定义 getXXX
方法或者 setXXX
方法。getter
和 setter
方法的作用就是提供了一种访问和修改私有属性的方式,增强了封装性。
继承
单继承和多继承:
在 C++
当中,被继承的类被称为基类,继承的类被称为派生类。
单继承就是
// 继承:
// 单继承:
class Derived: public Circle {};
int main(){
Derived derived;
derived.setRadius(2);
derived.getRadius();
}
// 多继承:
class Base1 {};
class Base2 {};
class MultiDerived : public Base1, public Base2 {};
虚基类和多重继承冲突解决:
对于虚基类就是为了解决多重继承冲突,它可以用来避免重复基类成员的多次实例化问题。举个例子:
class Base {};
class Derived1 : virtual public Base {};
class Derived2 : virtual public Base {};
class MultipleDerived : public Derived1, public Derived2 {}; // 解决多重继承冲突
Derived1
类和 Derived2
类都继承 Base
类, MultipleDerived
类继承 Derived1
类和 Derived2
类 这样我们的MultipleDerived
类就重复继承了Base
类。因为 C++
可以实现多继承,多继承肯定就会出现这种情况,所以引入虚基类是很有必要的。
多态
虚函数和纯虚函数:
虚函数:虚函数是一个类的成员函数,它允许在派生类中被重写,从而实现多态。
举个例子:
class Animal {
private:
std::string food;
std::string name;
public:
Animal(const std::string &f, const std::string &n) {
food = f;
name = n;
}
virtual void eat() {
std::cout << name << "喜欢吃:" << std::endl;
}
virtual void run() = 0;
virtual void getLactation() {
std::cout << "种类" << std::endl;
}
void setFood(std::string &f) {
food = f;
}
void setName(std::string &n) {
name = n;
}
std::string getFood() {
return food;
}
std::string getName() {
return name;
}
};
class Dog : public Animal {
public:
Dog(const std::string &n, const std::string &f, const std::string &l) : Animal(f, n) {
lactation = l;
}
void eat() override {
std::cout << getName() << "正在吃狗粮" << std::endl;
}
void run() override {
std::cout << getName() << "正在奔跑" << std::endl;
}
void getLactation() override {
std::cout << getName() << "所属种类:" << lactation << std::endl;
}
private:
std::string lactation;
};
int main() {
// 使用new创建Dog对象,并通过Animal指针访问
Animal *animal = new Dog("贵宾犬", "肉", "哺乳动物");
animal->eat();
animal->getLactation();
animal->run();
// 记得释放内存
delete animal;
return 0;
}
这里面需要注意的是
Dog(const std::string &n, const std::string &f, const std::string &l) : Animal(f, n)
派生类构造函数调用基类构造函数是通过初始化列表来完成的,而不是在构造函数体内部调用。
对于纯虚函数来说:
它是在基类中声明的虚函数,其声明形式为 virtual void run() = 0;
。由于纯虚函数没有实际的函数体(即没有默认实现),任何包含纯虚函数的类都是抽象类,并且抽象类不能被实例化。这意味着你不能创建抽象类的对象。
所以当你去执行
Animal animal1= new Animal("贵宾犬", "肉");
它是会报错的。
当一个派生类从含有纯虚函数的基类继承时,为了能够实例化这个派生类,它必须提供纯虚函数的具体实现。换句话说,派生类必须重写所有基类中的纯虚函数,否则派生类也将成为一个抽象类,并且不能被实例化。
虚析构函数
在涉及多态和指针的情况下,基类应当声明虚析构函数,以确保当删除派生类的实例时,正确的析构函数被调用,避免资源泄露或未定义行为。例如:
class Base {
public:
virtual ~Base() {} // 虚析构函数
};
运行时多态和静态多态:
运行时多态:
通常通过虚函数实现。创建了一个指向基类的指针或者引用,但是开发者可以将衍生类赋值给这个指针,并且调用基类中的虚函数。
在运行时,C++
会查找实际对象类型对应的函数,并调用那个版本的函数,这就是动态绑定。
#include <iostream>
class Base {
public:
virtual void func() {
std::cout << "基类\n";
}
};
class Derived : public Base {
public:
void func() override {
std::cout << "衍生类\n";
}
};
int main() {
Base *ptr;
ptr = new Base();
ptr->func(); // 输出 "基类"
ptr = new Derived();
ptr->func(); // 输出 "衍生类"
delete ptr;
return 0;
}
静态多态通常指的是函数重载。
函数重载允许你在同一个作用域内有多个同名函数,只要它们的参数列表不同。编译器在编译时就能确定调用哪个版本的函数,这是基于函数调用时所提供的参数类型和数量。
#include <iostream>
void example(int x) {
std::cout << "Called with int: " << x << '\n';
}
void example(double x) {
std::cout << "Called with double: " << x << '\n';
}
int main() {
example(5); // 调用example(int)
example(5.0); // 调用example(double)
return 0;
}
4. 输入输出流
标准输入输出流
使用std::cin
和std::cout
:
std::cin
和std::cout
是预定义的流对象,分别用于标准输入和标准输出。使用std::cin
可以从标准输入设备读取数据,而std::cout
用于将数据输出到标准输出设备。
举例:
int main(){
int number;
std::cout << "请输入数字:";
std::cin >> number;
std::cout << "输入数字为:" << number << std::endl;
return 0;
};
格式化输出和输入:
常用方法:
- 设置宽度:
setw()
- 设置填充字符:
setfill()
- 设置精度:
setprecision()
- 设置浮点数格式:
fixed
和scientific
- 设置布尔值显示:
boolalpha
示例代码:
#include <iostream>
#include <iomanip>
int main() {
int number = 42;
double pi = 3.14159265358979323846;
double large_number = 1234567890.123456789;
std::cout << "Number: (no use function setw())" << number << '\n';
std::cout << "Number: (use function setw())" << std::setw(42) << number << '\n';
// 设置填充字符为 '*'
std::cout.fill('*');
std::cout << "Number with fill: " << std::setw(10) << number << '\n';
// 一般 fixed 和 scientific 都配合 setprecision() 方法使用
// 设置了 std::fixed 和 std::setprecision(n),那么小数点后将保留 n 位数字,不论数值大小如何。
// 设置了 std::scientific 和 std::setprecision(n),那么小数点后将保留 n 位数字,而数值则以科学记数法的形式显示。
std::cout << std::fixed << std::setprecision(2) << "Pi (fixed): " << pi << '\n';
std::cout << std::scientific << std::setprecision(2) << "Large Number (scientific): " << large_number << '\n';
std::cout << "Boolean: " << std::boolalpha << true << '\n';
return 0;
}
运行结果:
文件I/O
打开和关闭文件流:
std::ifstream
:
用于从文件读取数据。
std::ofstream
:
用于向文件写入数据。
std::fstream
:
结合了ifstream
和ofstream
的功能,既可以读取也可以写入文件。
读写文本文件和二进制文件:
读写文本文件示例代码:
#include <iostream>
#include <fstream>
int main(){
std::ofstream out("data.txt");
out << "Hello, World!\n";
out.close();
std::ifstream in("data.txt");
std::string line;
while (getline(in, line)) {
std::cout << line << '\n';
}
in.close();
return 0;
}
读写二进制文件示例代码:
#include <iostream>
#include <fstream>
struct Data {
int number;
double value;
};
int main(){
Data myData = {123,45.789};
std::ofstream binaryOut("binary_data.dat", std::ios::binary); // 创建 binaryOut 对象,定义写入的文件和写入的格式。
// 对我们创建的Data实例的指针进行转换为char类型,然后 binaryOut.write() 函数可以使用这个指针来写入 sizeof(Data) 字节的数据到文件中。
// 这样,整个结构体 myData 的内存表示就被原封不动地写入到了文件中。
binaryOut.write(reinterpret_cast<char*>(&myData),sizeof(Data));
binaryOut.close();
Data readData;
std::ifstream binaryIn("binary_data.dat", std::ios::binary);
binaryIn.read(reinterpret_cast<char*>(&readData),sizeof(Data));
binaryIn.close();
std::cout << "Read data: " << readData.number << ", " << readData.value << '\n'; // 输出读取的数据
return 0;
}
文件定位和错误处理:
文件定位:
可以使用 seekg()
和 seekp()
来改变文件指针的位置,以及使用 tellg()
和 tellp()
来获取文件指针的当前位置。
#include <fstream>
#include <iostream>
int main(){
std::ofstream out("data.txt");
out << "Hello, world!\n";
out.close();
std::ifstream in("data.txt");
in.seekg(7); // 移动到文件的第7个字符位置
char ch;
in >> ch; // 读取'w'
std::cout << ch << '\n'; // 输出 'w'
in.close();
return 0;
}
错误处理:
可以检查文件流对象的状态位来判断是否发生了错误。例如,eof()
检查是否到达文件末尾,fail()
检查是否有任何错误发生等。
#include <iostream>
#include <fstream>
int main(){
std::ifstream in("data.txt");
if (!in) {
std::cerr << "打开文件出现问题\n";
return 1;
}
// 检查文件读取过程中的错误...
while (!in.eof()) {
char ch;
in >> ch;
if (in.fail()) {
std::cerr << "读取文件出现问题\n";
break;
}
std::cout << ch;
}
in.close();
return 0;
}
5. 总结
上面主要从几个方面来进行阐述 C++
的一些基础的知识和用法,包括 C++
语法基础,指针和引用,面向对象编程,输入输出流。
希望能对您的学习有帮助!如果有什么问题,欢迎您跟我一起交流交流!