⛳️第一章 C++入门
1.1 基本知识
1. C++是面向对象编程(OOP),特点如下:
- 封装和数据隐藏
- 继承和重写
- 多态
2. main()函数的返回类型可以是任意的数据类型,而且是唯一一个非void型【 即void main()】可以不用return,因为main()由操作系统直接控制,不能被其他函数调用。
3. '0'与"0"与0不同
- '0' 是字符,占1个字节
- "0"是字符串,占2个字节(末尾加上'\0')
- 0 是整型,占4个字节;等价于'\0' NULL
4. 常量在定义时必须初始化,如
const float pi = 3.14; //正确 而对于 const float pi; //error pi = 3.14; //error
5. 结构化程序设计=功能分解+逐步求精
6. 变量命名:
驼峰原则:myCar
匈牙利表记法:imyCar //imyCar表示为int型的imyCar
1.2 练习
【例1.1】hello world!
#include<iostream> using namespace std; int main(){ cout<<"hello world!"<<endl; return 0; }
【例1.2】3*a-2*b+1
#include <iostream> using namespace std; int main() { int a,b; cin>>a>>b; cout<<"3a-2b+1="<<3*a-2*b+1<<endl; return 0; }
【例1.3】最大值的平方根
强制转换:static_case<double>(int 型) ; 而不能是double(int 型);
#include <iostream> #include <cmath> using namespace std; int max(int a,int b); int main() { int a,b; cin>>a>>b; cout<<"Max value's sqrt is "<<sqrt(static_cast<double>(max(a,b)))<<endl; return 0; } int max(int a,int b) { return a>b?a:b; }
⛳️第二章 基本数据类型和输入输出
2.1 数据类型
注意typedef可以增加数据类型的别名
typedef int INT; //int a=10; INT a=10;
2.2 输入输出
输出控制
添加头文件#include <iomanip> + #include <iostream>
注意
- 输出%,则printf("%%");
setprecision、fixed
会影响接下来的所有浮点数的输出,直到下个setprecision、fixed
出现为止。setprecision(2)表示精确2位,如 11.235 则输出 11 3.14 输出 3.1
要求精确到小数点后n位,使用cout<<fixed<<setprecision(n)<<value;
/*输入输出简单示例*/ int a; cin>>a; cout<<"the value a is"<<a<<endl;
/*限定输出格式*/ #include <iostream> #include <iomanip> using namespace std; int main() { double a = 11.2345; cout << setprecision(2) << a << endl; // 精确输出2个,这里输出11 cout << fixed<<setprecision(2) << a << endl; // 保留2位小数,输出11.23 cout << setw(8) << a << endl; // 控制输出宽度为8,右对齐(默认) cout << right << setw(8) << a << endl; // 控制输出宽度为8,右对齐 cout << left << setw(8) << a << endl; // 控制输出宽度为8,左对齐 } /*输出 11 11.23 11.23 11.23 11.23 */
⛳️第三章 表达式和语句
3.1 基础知识
- 左值和右值:C/C++面试题之语言基础篇(一)-CSDN博客
- 算术运算类型的转换朝着更精确的方向进行。如1.0/2=浮点型数据
⛳️第四章 过程化语句
过程化语句和C语言一样:
- while语句
- do ...while语句
- for语句
详见第五章:【C语言】自学终极笔记-CSDN博客
【例】利用公司ㄇ=4*(1-1/3+1/5-1/7...),直到最后一项的绝对值<1e-8为止
#include<iostream> #include<cmath> using namespace std; double Pi_value(double n); int main(){ double n=1e-8; cout<<Pi_value(n)<<endl; return 0; } double Pi_value(double n) { double sum=1; double k=1; int sign=-1; int i=3; for(;abs(k)>=n;){ k=1.0/i; sum=sum+sign*k; sign=(-1)*sign; i=i+2; } return sum*4; }
【例】给定某数,判断是否为素数
#include<iostream> using namespace std; int SuNum(int n); int main(){ int n; cin>>n; int flag=SuNum(n); if(flag>0){ cout<<n<<"是素数"<<endl; } else { cout<<n<<"不是素数"<<endl; } return 0; } int SuNum(int n) { for(int i=2;i<n;i++){ if(n%i==0) { return -1; } } return 1; }
⛳️第五章 函数
5.1 基本知识
- 函数原型:即函数声明,如 int max(int a,int b); 等价于 int max(int ,int );
为什么用函数声明做函数原型? 便于对函数调用的合法性进行检查- 函数分两种:标准库函数+用户自定义函数
- 函数定义=函数声明+函数体
- 重载函数至少在参数个数、参数类型、参数顺序有所不同。
错误示例:
void func(int); int func(int);//返回类型不同则无法实现重载
默认参数的函数:
- 有默认值的参数应该位于参数列表的右侧
- 默认参数应该从右向左设置: 默认参数的赋值应该从右边的参数开始,不能跳过某个参数。在上述示例中,首先给
name
设置了默认值,然后是age
。- 默认参数只能在函数声明中出现一次: 默认参数只能在函数声明中出现一次,而不应该在函数定义中重复提供默认值。
/*默认参数的函数*/ #include <iostream> using namespace std; // 函数声明,参数有默认值,默认参数的赋值应该从右边的参数开始 void greet(string name = "Guest", int age = 25);//ok //void greet(string name, int age = 25);//ok //void greet(string name = "Guest", int age);//error int main() { greet();//Hello, Guest! Age: 25 greet("John");//Hello, John! Age: 25 greet("Alice", 30);//Hello, Alice! Age: 30 return 0; } // 函数定义,注意参数的默认值只需要在声明处提供就好,否则报错 void greet(string name, int age) { cout << "Hello, " << name << "! Age: " << age << endl; }
5.2 内联函数 inline
内联函数和宏函数 | 函数的区别:C/C++面试题之语言基础篇(一)-CSDN博客
#include <iostream> // 声明内联函数 inline int add(int a, int b) { return a + b; } int main() { int x = 5, y = 10; // 调用内联函数 int result = add(x, y); std::cout << "Sum: " << result << std::endl; return 0; }
⛳️第六章 程序结构
预处理程序:#include、#define、#if
其中#include两种格式:
- #inlcude <xxx>
- #inlcude "xxx"
区别:如果头文件是由开发者创建并与源文件位于同一项目中,使用双引号格式是比较常见的。而如果是标准库或系统提供的头文件,使用尖括号格式更为合适。
⛳️第七章 数组
⛳️第八章 指针
8.1 基本知识
8.2 new、delete用法
堆上创建内存分配使用new、delete【区别于C语言的malloc、free】
new/delete注意:
- 创建单个元素:int *a=new int;
- 创建一维数组:int *arr=new int[n];//n为数组具体大小
- 创建二维数组:
int **arr=new *int[n];//先创建arr[n] for(int i=0;i<n;i++){ arr[i]=new int[k]; }
- new调用的默认是C++提供的无参构造函数,当然也可以调用自己写的。
#include <iostream> using namespace std; class Person { public: // 带有参数的构造函数 Person(string name, int age) : name(name), age(age) { cout << "Person object created with name: " << name << ", age: " << age << endl; } // 析构函数 ~Person() { cout << "Person object destroyed." << endl; } private: string name; int age; }; int main() { // 使用带有参数的构造函数创建对象 Person *personPtr = new Person("John Doe", 25); // 使用对象... // 记得在不需要对象时释放内存 delete personPtr; return 0; }
简单示例:
1.单个元素
#include <iostream> using namespace std; int main() { int *a=new int; *a=5; cout<<*a<<endl;//5 cout<<a<<endl;//0x677d80 [内存值] delete a; return 0; }
2.一维数组
#include <iostream> using namespace std; int main() { int n=5; int *a=new int[n]; //a={0,1,2,3,4} is error for(int i=0;i<n;i++){ cin>>a[i]; } for(int i=0;i<n;i++){ cout<<a[i]<<" "; } cout<<endl; delete [] a; return 0; }
3.二维数组
#include <iostream> using namespace std; //赋值 void func(int **a,int n){ for(int i=0;i<n;i++){ for(int j=0;j<3;j++){ a[i][j]=i+j; } } } //输出二维 void Disp(int **a,int n){ for(int i=0;i<n;i++){ for(int j=0;j<3;j++){ cout<<a[i][j]<<" "; } cout<<endl; } } int main() { //创建a[n][k],先创建a[n],后创建各自的一维数组 int n=4; int k=n; int **a=new int*[n]; for (int i = 0; i < n; i++) { a[i] = new int[k]; } func(a,n); Disp(a,n); //释放,和创建顺序相反,即对称 for (int i = 0; i < n; i++) { delete[] a[i]; } delete [] a; return 0; } /*输出 0 1 2 1 2 3 2 3 4 3 4 5 */
简单示例
#include <iostream> using namespace std; class MyClass { public: MyClass() {//构造函数 cout << "Constructor called." << endl; } ~MyClass() {//析构函数 cout << "Destructor called." << endl; } }; int main() { /*********new \ delete************/ // 创建单个对象 MyClass* singleObject = new MyClass;//输出Constructor called. // 删除单个对象 delete singleObject;//输出Destructor called. // 创建对象数组 MyClass* arrayObjects = new MyClass[3];//输出3个Constructor called. // 删除对象数组 delete[] arrayObjects;//输出3个Destructor called. /*********malloc \ free************/ int *a = (int *)malloc(sizeof(int)); int *arr = (int *)malloc(sizeof(int) * 5); free(a); free(arr); return 0; }
⛳️第九章 引用
9.1 基本知识
1. 引用是个别名,不占存储空间:引用允许你通过不同的名字访问相同的内存位置,而不是创建一个新的存储空间。
如下面示例,b与a属于同一个地址
int a; int &b=a;//b是a的引用
2. 引用一旦维系便无法更改。后续的赋值也就仅仅是赋值而不是引用。
int a=5; int &b=a;//b是a的引用 int c=10; b=c;//则b=a=10
3. 不允许void 引用
void & a=3; //error
引用的简单示例:
#include <iostream> using namespace std; int main() { int originalVariable = 42; // 创建引用 int& reference = originalVariable; cout << "Original variable: " << originalVariable << endl;//42 cout << "Reference: " << reference << endl;//42 // 修改引用会修改原始变量 reference = 100; cout << "After modifying reference:" << endl; cout << "Original variable: " << originalVariable << endl;//100 cout << "Reference: " << reference << endl;//100 return 0; }
9.2 练习
引用做swap()完成值交换,传递的是地址 理解即可
#include <iostream> using namespace std; // 使用引用实现 swap 函数 void swap(int& a, int& b) { int temp = a; a = b; b = temp; } int main() { int num1 = 5; int num2 = 10; // 调用 swap 函数 swap(num1, num2); cout << "After swapping:" << endl; cout << "num1: " << num1 << endl;//10 cout << "num2: " << num2 << endl;//5 return 0; }
⛳️第十章 结构
C++结构体无需typedef后续定义也可以是Date date; 而c语言则是 struct Date today;
关于结构体的内容详见:【C语言】自学终极笔记第九章
⛳️第十一章 类
11.1 基本知识
- 类class默认是private;C语言的struct默认public
- 类名不能和函数名相同,但可以与形参同名。
- 对象(类class)=成员函数+成员变量
- 类的封装:类中有些成员是保护的,不能被外界直接修改(可以通过公共接口修改);另一些是公共的,提供接口供外界使用。
11.2 练习
1. 类中类外定义成员函数+继承+重写 看懂即可
#include <iostream> using namespace std; class MyBaseClass// 基类 { public: virtual void virtualFunction() { cout << "基类虚函数" << endl; } // 类外定义 void normalFunction(int x); // 重载函数 void normalFunction(double x) { cout << "基类 函数(double)" << x << endl; } private: int x; double y; }; // 在类外部定义成员函数: "类名::"放中间 void MyBaseClass::normalFunction(int x)//MyBaseClass:: void normalFunction() is error { cout << "基类 函数(int)" <<x<< endl; } class MyDerivedClass : public MyBaseClass// 派生类 { public: // 重写基类的虚函数 void virtualFunction() override { cout << "派生类 重写函数" << endl; } // 新增成员函数 void additionalFunction() { cout << "派生类 额外函数" << endl; } // 重载函数的派生类版本 void normalFunction(double y) { cout << "派生类 重写函数(double)" << y << endl; } }; int main() { MyDerivedClass derivedObj; derivedObj.virtualFunction(); // 派生类 重写函数 derivedObj.normalFunction(2); // 派生类 重写函数(double)2 derivedObj.additionalFunction(); // 派生类 额外函数 derivedObj.normalFunction(42.1); // 派生类 重写函数(double)42.1 return 0; }
2. 重载成员函数看懂即可
#include <iostream> using namespace std; class MyClass { public: void display() { cout << "This is the original display function." << endl; } // 重载display函数,不同参数列表 void display(int value) { cout << "Displaying value: " << value << endl; } }; int main() { MyClass myObject; myObject.display(); // This is the original display function. myObject.display(42); // Displaying value: 42 return 0; }
3. 用指针和引用调用成员函数的示例
#include <iostream> using namespace std; class MyClass { public: void display() { cout << "This is the function" << endl; } }; int main() { MyClass myObject; // 使用指针调用成员函数 MyClass* ptr = &myObject; ptr->display(); //This is the function (*ptr).display();//This is the function // 使用引用调用成员函数 MyClass& ref = myObject; ref.display(); //This is the function return 0; }
⛳️第十二章 构造函数
12.1 基本知识
- 构造函数作用:创建+初始化类对象
析构函数作用:撤销类对象- 构造函数、析构函数可以在类内和类外定义
构造函数:
- 可以有参数
- 无返回值,但可以有 "return;"
- 无函数类型
- 自动调用,格式为 类名
- 允许重载
析构函数:
- 没有参数
- 无函数类型
- 自动调用,格式为 ~类名
- 不能重载
注意:
- C++的每个类都必须要有构造函数,若用户未提供则系统提供一个默认的无参构造函数【用户提供则系统不再默认提供】
- 对于无参构造函数的创建
Tdate today;//ok Tdate today();//error
- 对于静态成员变量,只能在类内声明,类外初始化
#include <iostream> using namespace std; class MyClass { public: // 静态成员变量的声明 static int staticVar; // 静态成员函数 static void staticFunction() { cout << "Static function called." << endl; } }; // 静态成员变量的定义和初始化 int MyClass::staticVar = 42; int main() { // 调用静态成员函数 MyClass::staticFunction(); // 访问静态成员变量 cout << "Static variable value: " << MyClass::staticVar << endl; return 0; } /*输出 Static function called. Static variable value: 42 */
12.2 练习
1. 多个类,其中一个类的数据成员包含其他类对象,调用构造函数是依次调用,析构函数顺序与构造函数调用顺序相反
#include <iostream> using namespace std; class InnerClass { public: InnerClass() { cout << "1Class Constructor called." << endl; } ~InnerClass() { cout << "1Class Destructor called." << endl; } }; class OuterClass { public: OuterClass() { cout << "2Class Constructor called." << endl; } ~OuterClass() { cout << "2Class Destructor called." << endl; } private: InnerClass innerObject;//类的数据成员包含另一个类对象 }; int main() { // 创建 outerObject 对象,触发构造函数 OuterClass outerObject; // 对象在 main 函数结束时销毁,触发析构函数 return 0; } /*输出 1Class Constructor called. 2Class Constructor called. 2Class Destructor called. 1Class Destructor called. */
2. 析构函数简单示例
#include <iostream> using namespace std; class MyClass { private: int* dynamicArray; public: // 构造函数 MyClass(int size) { dynamicArray = new int[size]; cout << "MyClass Constructor called." << endl; } // 析构函数 ~MyClass() { delete[] dynamicArray; cout << "MyClass Destructor called." << endl; } }; int main() { // 创建 MyClass 对象 MyClass myObject(5); // 对象在 main 函数结束时销毁,触发析构函数调用 return 0; } /*输出 MyClass Constructor called. MyClass Destructor called. */
3. 构造函数重载简单示例
#include <iostream> using namespace std; class MyClass { private: int value; public: // 默认构造函数 MyClass() { value = 0; cout << "Default Constructor called." << endl; } // 带参数的构造函数 MyClass(int initValue) { value = initValue; cout << "Constructor called with value: " << value << endl; } // 另一个带两个参数的构造函数 MyClass(int initValue, bool isSpecial) { if (isSpecial) { value = initValue * 2; } else { value = initValue; } cout << "Special Constructor called with value: " << value << endl; } }; int main() { // 使用不同构造函数创建 MyClass 对象 MyClass defaultObject; // 默认构造函数 MyClass objectWithParam(42); // 带参数的构造函数 MyClass specialObject(30, true); // 另一个带两个参数的构造函数 return 0; } /*输出 Default Constructor called. Constructor called with value: 42 Special Constructor called with value: 60 */
⛳️第十三章 面向对象程序设计
13.1 基本知识
抽象:抽象的主要目标是提供一种清晰的、高层次的接口,使得实现细节可以被隐藏,同时允许派生类提供特定的实现。
13.2 练习
1. 纯虚函数: 纯虚函数是在基类中声明但没有实现的虚函数,通过在声明中使用
= 0
来标记。任何包含纯虚函数的类都被认为是抽象类,不能被实例化。
class AbstractClass { public: // 纯虚函数 virtual void pureVirtualFunction() const = 0; };
2. 抽象类: 抽象类是包含纯虚函数的类。抽象类不能被实例化,它用于定义接口,由派生类提供具体实现。
class AbstractClass { public: // 纯虚函数,使类成为抽象类 virtual void abstractFunction() const = 0; // 普通成员函数 void commonFunction() const { // 具体实现 } };
⛳️第十四章 堆与拷贝构造函数
14.1 基本知识
堆:
- malloc/free和new/delete区别详见:C/C++面试题之语言基础篇(一)-CSDN博客
- malloc、free不会调用构造函数和析构函数
- new 对象数组 调用的构造函数只能是 默认的构造函数,没有提供则出错【不写C++会提供默认的无参构造函数,但用户自己给了则不再提供默认的无参构造函数】
拷贝构造函数:
拷贝场景一:对象可以初始化另一个对象
Tdate day1(12,3,1997); Tdate day2=day1;//day1去初始化day2
拷贝场景二:对象需要做参数传递
void func(Tdate day){} int main() { Tdate day1; func(day1);//传递给形参day }
基本概念:
- 拷贝构造函数参数应该是Tdate(Tdate &day);而非Tdate(Tdate *day);
- C++提供默认拷贝构造函数(浅拷贝)
- 深拷贝与浅拷贝,区别详见C/C++面试题之语言基础篇(一)-CSDN博客 示例见14.3
- 建议在拷贝构造函数中使用
const
修饰符。这可以防止在拷贝过程中修改原始对象。如
class A{ A(const A &ohter){//拷贝构造函数 ...} ... };
14.2 堆练习
malloc、free不会调用构造函数和析构函数示例
#include <iostream> #include <cstdlib> using namespace std; class MyClass { public: MyClass() { cout << "Constructor called." << endl; } ~MyClass() { cout << "Destructor called." << endl; } void display() { cout << "Displaying." << endl; } }; int main() { /*错误示例*/ // 使用 malloc 分配内存 MyClass* myObject = (MyClass*)malloc(sizeof(MyClass)));//不会调用构造函数 // 使用 free 释放内存(析构函数不会被调用) free(myObject); /*正确示例*/ // 使用 new 运算符分配内存并调用构造函数 MyClass* myObject = new MyClass; // 使用 delete 运算符释放内存并调用析构函数 delete myObject; return 0; }
14.3 拷贝构造函数练习
拷贝构造函数简单示例
#include <iostream> using namespace std; class MyClass { private: int* data; public: // 构造函数 MyClass(int value) { data = new int(value); cout << "Constructor called with value: " << *data << endl; } // 拷贝构造函数 MyClass(MyClass& other) { data = new int(*(other.data)); cout << "Copy Constructor called. Copied value: " << *data << endl; } // 析构函数 ~MyClass() { cout << "Destructor called. Value: " << *data << endl; delete data; } }; void fn(MyClass s){ cout<<"fn message"<<endl; } int main() { // 创建对象 MyClass originalObject(42);//Constructor called with value: 42 // 使用拷贝构造函数创建新对象 fn(originalObject);//Copy Constructor called. Copied value: 42 return 0; } /*输出 Constructor called with value: 42 Copy Constructor called. Copied value: 42 fn message Destructor called. Value: 42 Destructor called. Value: 42 */
默认拷贝构造函数【浅拷贝】
#include <iostream> using namespace std; class MyClass { public: // 构造函数 MyClass(int value) : data(value) { cout << "Constructor called with value: " << data << endl; } //拷贝构造函数 MyClass(MyClass &other){ data=other.data; } // 析构函数 ~MyClass() { cout << "Destructor called for value: " << data << endl; } private: int data; }; int main() { // 创建对象 MyClass originalObject(42); // 使用默认拷贝构造函数创建新对象 MyClass copiedObject = originalObject; return 0; } /*输出 Constructor called with value: 42 Destructor called for value: 42 Destructor called for value: 42 */
深拷贝简单示例:在拷贝构造函数加入new 分配堆资源
#include <iostream> #include <cstring> using namespace std; class MyString { public: // 构造函数 MyString(char* value) { size = strlen(value); data = new char[size + 1]; strcpy(data, value); cout << "Constructor called with value: " << data << endl; } // 拷贝构造函数 MyString(MyString& other) { size = other.size; data = new char[size + 1];//如果这里不加则为浅拷贝,后续delete会出错 strcpy(data, other.data); cout << "Copy Constructor called. Copied value: " << data << endl; } // 析构函数 ~MyString() { cout << "Destructor called for value: " << data << endl; delete[] data; } private: char* data; size_t size; }; int main() { // 创建对象 MyString originalObject("Hello"); // 使用拷贝构造函数创建新对象 MyString copiedObject = originalObject; return 0; } /*输出 Constructor called with value: Hello Copy Constructor called. Copied value: Hello Destructor called for value: Hello Destructor called for value: Hello */
⛳️第十五章 静态成员与友元
15.1 基本知识
静态数据成员:
- 类中声明,类外初始化(不能在任何函数内分配空间+初始化)。
- const 静态数据成员: 静态数据成员可以声明为 const,必须在类中声明时初始化。
class MyClass { public: static const int constStaticData = 42; };
静态成员函数:
- 访问权限: 静态成员函数只能访问静态成员和静态函数,而不能访问非静态成员或非静态函数。
- this 指针: 静态成员函数没有隐含的
this
指针。- 调用方式: 静态成员函数可以通过类名直接调用,而不需要通过类的实例。例如:
ClassName::staticMemberFunction()
。- 不能声明为 const 或 volatile: 静态成员函数不能被声明为
const
或volatile
,因为这两个关键字都与实例相关。静态数据成员和静态成员函数的根本区别:
静态数据成员有this指针,而静态成员函数无this指针。
友元(friend关键字)
- 破坏封装性: 友元机制破坏了类的封装性,因为允许外部实体访问类的一切成员。
- 不是成员函数,友元声明可以在类中任何位置(效果都一样),定义在类外
15.2 练习
静态成员的简单示例。
#include <iostream> using namespace std; class MyClass { public: // 静态数据成员的声明 static int staticData; // 静态成员函数,用于访问和修改静态数据成员 static void printStaticData() { cout << "Static Data: " << staticData << endl; } }; // 静态数据成员的初始化 int MyClass::staticData = 0; int main() { // 创建类的对象 MyClass obj1, obj2; MyClass::staticData = 42;// 通过类名访问静态数据成员 obj1.staticData=43;// 通过对象访问静态数据成员 cout << "Object 1 Static Data: " << obj1.staticData << endl; cout << "Object 2 Static Data: " << obj2.staticData << endl; // 通过静态成员函数访问和修改静态数据成员 MyClass::printStaticData(); MyClass::staticData = 100; MyClass::printStaticData(); return 0; } /*输出 Object 1 Static Data: 43 Object 2 Static Data: 43 Static Data: 43 Static Data: 100 0 */
友元的简单示例
#include <iostream> class MyClass { private: int privateData; friend void friendFunction(const MyClass& obj); // 友元函数声明 public: MyClass() : privateData(0) {}//构造函数,privateData默认初始化为0 void setPrivateData(int value) { privateData = value; } }; // 友元函数的定义 void friendFunction(const MyClass& obj) { std::cout << "Friend Function Accessing Private Data: " << obj.privateData << std::endl; } int main() { MyClass obj; obj.setPrivateData(42); // 友元函数的调用 friendFunction(obj); return 0; } /*输出 Friend Function Accessing Private Data: 42 */
⛳️第十六章 继承
16.1 基本知识
继承:
派生类(子类)继承基类(父类)的成员函数和数据成员,并在此基础上可以构建自己的成员函数和数据成员。避免了一些重复性的工作。
class A{ //... }; //单个继承 class B : public A{//公有继承:B继承A的成员函数和数据成员 //... } class c : protected A{//保护继承:c继承A的成员函数和数据成员 //... } class d : private A{//私有继承:d继承A的成员函数和数据成员 //... } //多重继承 class B : public A,public E{//公有继承:B继承A+E的成员函数和数据成员 //... }
- 继承后派生类能够访问父类的public、protected成员,不能访问private成员
- 而类外普通函数、对象能够访问父类的public成员,不能访问private、protected成员
不管哪种继承方式,父类的私有成员都不可以访问
派生类的构造:
会依次调用父类的构造函数,析构与构造顺序相反。见12.2练习1
虚拟继承:
虚拟继承用于解决由多重继承导致的菱形继承问题。虚拟继承通过关键字
virtual
实现,可以确保在继承体系中共享相同基类的实例只有一份。
class A{ //... }; //单个继承 class B : virtual public A{//公有继承:B继承A的成员函数和数据成员 //... } class c : virtual public A{//保护继承:c继承A的成员函数和数据成员 //... } //多重继承:虚拟继承 class B : public A,public E{//公有继承:B继承A+E的成员函数和数据成员 //... }
16.2 练习
继承的简单示例
#include <iostream> // 基类(父类) class Animal { public: Animal(const std::string& name) : name(name) {} void eat() const { std::cout << name << " is eating." << std::endl; } void sleep() const { std::cout << name << " is sleeping." << std::endl; } private: std::string name; }; // 派生类(子类) class Dog : public Animal { public: Dog(const std::string& name, const std::string& breed) : Animal(name), breed(breed) {} void bark() const { std::cout << "Woof! Woof!" << std::endl; } private: std::string breed; }; int main() { // 创建派生类对象 Dog myDog("Buddy", "Golden Retriever"); // 调用基类的成员函数 myDog.eat(); myDog.sleep(); // 调用派生类自己的成员函数 myDog.bark(); return 0; } /*输出 Buddy is eating. Buddy is sleeping. Woof! Woof! */
虚拟继承的简单示例
#include <iostream> using namespace std; // 基类 Animal,加入数据成员 class Animal { protected: string species; // 动物的种类 public: Animal(const string& species) : species(species) {} // 虚拟函数,表示动物的叫声 virtual void makeSound() const { cout << "Animal makes a sound" << endl; } }; // 虚拟继承方式1 class Mammal : public virtual Animal { public: Mammal(const string& species) : Animal(species) {}//构造函数 // 虚拟函数,表示哺乳动物的行为 virtual void nurse() const { cout << "Mammal nurses its young" << endl; } }; // 虚拟继承方式2 class Bird : public virtual Animal { public: Bird(const string& species) : Animal(species) {}//构造函数 // 虚拟函数,表示鸟类的飞行 virtual void fly() const { cout << "Bird flies in the sky" << endl; } }; // 最终派生类,这样设计确保了最终派生类 Bat 中只包含一个共享的 Animal 子对象【来自于最远的、最顶层的虚拟基类】,避免了菱形继承问题。 class Bat : public Mammal, public Bird {//虚拟继承 public: Bat(const string& species) : Animal(species), Mammal(species), Bird(species) {}//构造函数 // 重写虚拟函数,表示蝙蝠的叫声 void makeSound() const override { cout << "Bat makes a high-pitched sound" << endl; } }; int main() { // 创建蝙蝠对象 Bat bat("Bat"); // 调用虚拟函数 bat.makeSound(); // 蝙蝠特有的叫声 bat.nurse(); // 继承自哺乳动物 bat.fly(); // 继承自鸟类 return 0; } /*输出 Bat makes a high-pitched sound Mammal nurses its young Bird flies in the sky */
⛳️第十七章 多态
17.1 基本知识
虚函数:
- 在基类中通过使用
virtual
关键字声明虚函数,virtual
关键字基类必须要用,而派生类可以省略。
class Base { public: virtual void display() const { // 虚函数的实现 } };
- 派生类可以重写基类中的虚函数,提供自己的实现【函数原型必须完全一样,区别于函数重载(参数个数、参数顺序、参数类型至少有一个不同)】。在派生类中,使用
override
关键字可以显式地表明这是对基类虚函数的重写【也可以没有】。
class Derived : public Base { public: void display() const{ // 派生类对虚函数的实现 //... } //显式重写则是 void display() const override { // 派生类对虚函数的实现 //... } };
- 虚函数不能是静态成员函数、内联函数: 虚函数必须是非静态成员函数。虚函数的调度机制是通过对象的虚函数表(vtable)来实现的,而静态成员函数不属于对象的实例,因此不能是虚函数。
- 构造函数不能是虚函数: 构造函数不能是虚函数。在对象构造的过程中,虚表还没有被构建,因此无法实现虚函数的多态性。
- 析构函数应该声明为虚函数: 如果类中包含虚函数,通常应该将析构函数声明为虚函数。这确保在使用基类指针指向派生类对象时,可以正确调用派生类的析构函数,避免内存泄漏。
纯虚函数:
纯虚函数本身在基类中没有具体的实现,而是在派生类中被强制要求实现。
纯虚函数的声明和定义的一般形式如下:
class AbstractBase { public: virtual void pureVirtualFunction() const = 0; // 纯虚函数声明 virtual ~AbstractBase() {} // 虚析构函数 };
注意:
- 在声明纯虚函数时,在函数声明的末尾使用
= 0
表示这是一个纯虚函数- 要求在派生类中被强制要求实现
- 如果一个类中包含了纯虚函数,它就成为抽象类
多态:
多态允许不同类型的对象调用同一函数或操作能够产生不同的响应。在C++中,主要通过虚函数(Virtual Function)来实现多态性。
分成运行时多态和静态多态
- 运行时多态【多态的主要形式,也称动态多态】:主要体现:虚函数和继承。通过使用指向基类的指针或引用,调用相同的虚函数时,根据实际对象类型来确定调用哪个版本的函数。
int main() { Base* ptr = new Derived(); // 指向派生类对象的基类指针 ptr->display(); // 调用派生类的实现,而不是基类的实现 delete ptr; return 0; }
- 编译时多态【静态多态】:主要体现:函数重载和模板实现。在编译时确定调用哪个函数。
void print(int value) { // 实现1 } void print(double value) { // 实现2 } int main() { print(42); // 调用 print(int value) print(3.14); // 调用 print(double value) return 0; }
17.2 练习
虚函数+村虚函数简单示例
#include <iostream> using namespace std; // 基类 class Shape { public: // 纯虚函数,表示计算面积 virtual double area() const = 0; // 虚函数,用于显示形状信息 virtual void display() const { cout << "Shape" << endl; } // 虚析构函数 virtual ~Shape() {} }; // 圆形类,继承自基类 class Circle : public Shape { private: double radius; public: Circle(double r) : radius(r) {} // 实现基类中的纯虚函数 double area() const override { return 3.14 * radius * radius; } // 重写基类中的虚函数 void display() const override { cout << "Circle with radius " << radius << endl; } }; // 矩形类,继承自基类 class Rectangle : public Shape { private: double length; double width; public: Rectangle(double l, double w) : length(l), width(w) {} // 实现基类中的纯虚函数 double area() const override { return length * width; } // 重写基类中的虚函数 void display() const override { cout << "Rectangle with length " << length << " and width " << width << endl; } }; int main() { // 通过基类指针实现多态 Shape* shape1 = new Circle(5.0); Shape* shape2 = new Rectangle(4.0, 6.0); // 调用虚函数,实现多态性 shape1->display(); // 输出:Circle with radius 5 cout << "Area: " << shape1->area() << endl; // 输出:Area: 78.5 shape2->display(); // 输出:Rectangle with length 4 and width 6 cout << "Area: " << shape2->area() << endl; // 输出:Area: 24 // 释放动态分配的内存 delete shape1; delete shape2; return 0; }
⛳️第十八章 运算符重载
18.1 基本知识
运算符重载
- 优先级和结合性、操作个数保持不变。
- 不能重载运算符:点操作(.)、域操作(::)、条件操作符(?)等等
- 重载形式
returnType operator op(parameters) { // 运算符的新实现 } /*比如 class A{ ... }; int poerator +(A&,A&); */
18.2 练习
复数加法:重载加法运算符
+
#include <iostream> using namespace std; class Complex { private: double real; double imag; public: Complex(double r, double i) : real(r), imag(i) {} // 重载加法运算符 Complex operator+(const Complex& other) const { return Complex(real + other.real, imag + other.imag); } void display() const { cout << real << " + " << imag << "i" << endl; } }; int main() { // 创建两个复数对象 Complex c1(3.0, 4.0); Complex c2(1.5, 2.5); // 使用重载的加法运算符 Complex result = c1 + c2; // 显示结果 cout << "Result of addition: "; result.display(); return 0; } /*输出 Result of addition: 4.5 + 6.5i */
⛳️第十九章 I/O流
19.1 基本知识
待续...
19.2 练习
待续...
⛳️第二十章 模板
20.1 基本知识
模板
- 分为类模板+函数模板
使用模板的优势:
- 通用性: 模板使得可以编写适用于多种数据类型的通用代码,而不需要为每种数据类型编写特定的代码。
template <typename T> T add(T a, T b) { return a + b; } int result_int = add(3, 4); double result_double = add(3.5, 4.5);
- 灵活性: 模板提供了一种在编译时实现多态性的方式。通过在编译时生成特定的代码版本,可以避免运行时的性能开销,并在编译时进行类型检查。
- 代码重用: 使用模板可以创建通用的数据结构和算法,以适应不同的需求。这样可以减少代码的复制粘贴,提高代码的重用性。
template <typename T> class Pair { private: T first; T second; public: Pair(const T& f, const T& s) : first(f), second(s) {} }; Pair<int> intPair(1, 2); Pair<double> doublePair(3.5, 4.5);
类模板:
以下是类模板的一般语法:
template <typename T1, typename T2, ...> class ClassName { // 类成员和成员函数的定义 public: ClassName(T1 param1, T2 param2, ...); // 其他成员函数或声明 };
其中,
T1
,T2
, ... 是模板参数列表,用逗号分隔。这些模板参数可以在类定义中的成员变量、成员函数、构造函数等地方使用,起到泛型的作用。函数模板:
- 函数模板的一般语法如下:
template <typename T> T functionName(T param1, T param2, ...) { // 函数体 }
- 其中,
typename T
表示模板参数,T
可以是任何合法的标识符,用于表示函数的参数和返回类型。在实际调用时,编译器会根据传入的参数类型,自动推导出正确的类型。- 待续
函数模板和模板函数区别
- 函数模板: 函数模板是模板的定义。创建通用函数的机制,其中函数的定义使用模板参数。这使得函数能够接受不同类型的参数,从而实现对多种数据类型的通用操作。函数模板使用
template
关键字声明,并且可以包含一个或多个类型参数。
template <typename T> T add(T a, T b) { return a + b; }
- 模板函数: 模板函数是函数定义。指通过函数模板实例化得到的具体函数。在调用函数时,编译器会根据传递的参数类型自动生成相应的函数版本。
int result_int = add(3, 4); // 实例化为 int 版本 double result_double = add(3.5, 4.5); // 实例化为 double 版本
类模板和模板类区别
- 类模板: 类模板是模板定义。一种创建通用类的机制,其中类的定义使用模板参数。这使得类能够处理不同类型的数据,从而实现对多种数据类型的通用数据结构或算法。类模板使用
template
关键字声明,并且可以包含一个或多个类型参数。
template <typename T> class Pair { private: T first; T second; public: Pair(const T& f, const T& s) : first(f), second(s) {} };
- 模板类:模板类是类定义。指通过类模板实例化得到的具体类。在使用类时,可以为类的模板参数指定具体的类型,从而实例化得到特定的类。
Pair<int> intPair(1, 2); // 实例化为处理 int 类型的 Pair Pair<double> doublePair(3.5, 4.5); // 实例化为处理 double 类型的 Pair
20.2 练习
函数模板简单示例
#include <iostream> using namespace std; // 函数模板 template <typename T> T findMin(T a, T b) { return (a < b) ? a : b; } int main() { // 使用模板函数 int intMin = findMin(3, 7); double doubleMin = findMin(4.5, 2.7); // 显示结果 cout << "Minimum of integers: " << intMin << endl; cout << "Minimum of doubles: " << doubleMin << endl; return 0; } /*输出 Minimum of integers: 3 Minimum of doubles: 2.7 */
类模板简单示例
#include <iostream> using namespace std; // 类模板的定义 template <typename T> class Pair { private: T first; T second; public: Pair(const T& f, const T& s) : first(f), second(s) {} //将成员变量 first 和 second 初始化为传递进来的参数值 f 和 s。 T getFirst() const { return first; } T getSecond() const { return second; } }; int main() { // 使用类模板实例化具体的类型 Pair<int> intPair(1, 2); Pair<double> doublePair(3.14, 2.71); // 访问成员函数 cout << "Int Pair: " << intPair.getFirst() << ", " << intPair.getSecond() << endl; cout << "Double Pair: " << doublePair.getFirst() << ", " << doublePair.getSecond() << endl; return 0; } /*输出 Int Pair: 1, 2 Double Pair: 3.14, 2.71 */
📝总结
嘘,听说有一位C++大师突破了次元壁,成功通关了编程的炼金之路!从入门到进阶,你的代码之旅如同编程宇宙的星空,熠熠生辉。你不仅掌握了代码的秘法,更诠释了编程的独特魔力。每一次Debug都是一场魔法修炼,每一行代码都是一篇炫目的咒语。恭喜你,编程冒险家,你已经成为这片代码大陆的传奇英雄。未来等着你用键盘书写更加壮丽的代码史
诗,展开属于你的数字冒险。继续释放编码魔法,让代码的光芒照亮前行的路途!