【C++】自学终极笔记

阅读: 评论:0

【C++】自学终极笔记

【C++】自学终极笔记

📋 前言

 🌈个人主页:Sarapines Programmer

 🔥 系列专栏:本期文章收录在《C++闯关笔记》,大家有兴趣可以浏览和关注,后面将会有更多精彩内容!

 ⏰翰墨致赠:文墨扬长风,情随碧波舞。江山孕雄心滚滚,志立云霄梦遨游。笔翰激雷鸣,豪情犹似澎湃浪涛,扬帆挟梦翱翔云际。


 🎉欢迎大家关注🔍点赞👍收藏⭐️留言📝
 🔔作者留言:

欢迎来到我的【C++】魔法学堂!这里是探索编程世界的秘境,我的学习笔记博客为你打开C++的魔法之门。在这里,我不仅分享C++的基础知识和高级技巧,还有着涉猎实用技术和项目经验的魔法药水。无论你是新手还是编程巫师,这个魔法堂会为你施展出奇幻的学习魔法,帮助你在C++的魔法森林中踏上一场奇妙之旅。准备好了吗?跟着我,让我们一起编织属于自己的魔法代码!

目录


📋 前言

⛳️第一章 C++入门

1.1 基本知识

1.2 练习

⛳️第二章 基本数据类型和输入输出

2.1 数据类型

2.2 输入输出

⛳️第三章 表达式和语句

3.1 基础知识

⛳️第四章 过程化语句

⛳️第五章 函数

5.1 基本知识

5.2 内联函数 inline

⛳️第六章 程序结构

⛳️第七章 数组

⛳️第八章 指针

8.1 基本知识

8.2 new、delete用法

⛳️第九章 引用

9.1 基本知识

9.2 练习

⛳️第十章 结构

⛳️第十一章 类

11.1 基本知识

11.2 练习

⛳️第十二章 构造函数

12.1 基本知识

12.2 练习

⛳️第十三章 面向对象程序设计

13.1 基本知识

13.2 练习

⛳️第十四章 堆与拷贝构造函数

14.1 基本知识

14.2 堆练习

14.3 拷贝构造函数练习

⛳️第十五章 静态成员与友元

15.1 基本知识

15.2 练习

⛳️第十六章 继承

16.1 基本知识

16.2 练习

⛳️第十七章 多态

17.1 基本知识

17.2 练习

⛳️第十八章 运算符重载

18.1 基本知识

18.2 练习

⛳️第十九章 I/O流

19.1 基本知识

19.2 练习

⛳️第二十章 模板

20.1 基本知识

20.2 练习

📝总结


⛳️第一章 C++入门

1.1 基本知识

1. C++是面向对象编程(OOP)特点如下:

  1. 封装和数据隐藏
  2. 继承和重写
  3. 多态

2. main()函数的返回类型可以是任意的数据类型,而且是唯一一个非void型【 即void main()】可以不用return,因为main()由操作系统直接控制,不能被其他函数调用。

3. '0'与"0"与0不同

  • '0'  是字符,占1个字节
  • "0"是字符串,占2个字节(末尾加上'')
  •  0 是整型,占4个字节;等价于''  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 数据类型

见【C语言】第三章 3.2

注意typedef可以增加数据类型的别名

typedef int INT;
//int a=10;
INT a=10;

2.2 输入输出

输出控制

添加头文件#include <iomanip>  +  #include <iostream>

注意

  1. 输出%,则printf("%%");
  2. setprecision、fixed会影响接下来的所有浮点数的输出,直到下个setprecision、fixed出现为止。
  3. setprecision(2)表示精确2位,如 11.235 则输出 11   3.14 输出 3.1
  4. 要求精确到小数点后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个,这里输出11cout << fixed<<setprecision(2) << a << endl; // 保留2位小数,输出11.23cout << setw(8) << a << endl;          // 控制输出宽度为8,右对齐(默认)cout << right << setw(8) << a << endl; // 控制输出宽度为8,右对齐cout << left << setw(8) << a << endl; // 控制输出宽度为8,左对齐
}/*输出
11
11.2311.2311.23
11.23
*/

⛳️第三章 表达式和语句

3.1 基础知识

  1. 左值和右值:C/C++面试题之语言基础篇(一)-CSDN博客 
  2. 算术运算类型的转换朝着更精确的方向进行。如1.0/2=浮点型数据

⛳️第四章 过程化语句

过程化语句和C语言一样:

  • while语句
  • do ...while语句
  • for语句

        详见第五章:【C语言】自学终极笔记-CSDN博客

【例】利用公司ㄇ=4*(1-1/3+1/),直到最后一项的绝对值<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 基本知识

  1. 函数原型:即函数声明,如  int max(int a,int b);    等价于    int max(int ,int ); 

    为什么用函数声明做函数原型?    便于对函数调用的合法性进行检查

  2. 函数分两种:标准库函数+用户自定义函数
  3. 函数定义=函数声明+函数体
  4. 重载函数至少在参数个数、参数类型、参数顺序有所不同。

    错误示例:

    void func(int);
    int func(int);//返回类型不同则无法实现重载

默认参数的函数:

  1. 有默认值的参数应该位于参数列表的右侧
  2. 默认参数应该从右向左设置: 默认参数的赋值应该从右边的参数开始,不能跳过某个参数。在上述示例中,首先给 name 设置了默认值,然后是 age

  3. 默认参数只能在函数声明中出现一次: 默认参数只能在函数声明中出现一次,而不应该在函数定义中重复提供默认值。

/*默认参数的函数*/
#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);//errorint main() {greet();//Hello, Guest! Age: 25greet("John");//Hello, John! Age: 25greet("Alice", 30);//Hello, Alice! Age: 30return 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两种格式:

  1. #inlcude <xxx>
  2. #inlcude "xxx"

区别:如果头文件是由开发者创建并与源文件位于同一项目中,使用双引号格式是比较常见的。而如果是标准库或系统提供的头文件,使用尖括号格式更为合适。


⛳️第七章 数组

详见:【C语言】自学终极笔记第六章


⛳️第八章 指针

8.1 基本知识

详见:【C语言】自学终极笔记第八章

8.2 new、delete用法

堆上创建内存分配使用new、delete【区别于C语言的malloc、free

new/delete注意:

  1. 创建单个元素:int *a=new int;
  2. 创建一维数组:int *arr=new int[n];//n为数组具体大小
  3. 创建二维数组:int **arr=new *int

简单示例:

1.单个元素

#include <iostream>using namespace std;
int main() {int *a=new int;*a=5;cout<<*a<<endl;//5cout<<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 errorfor(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;//42cout << "Reference: " << reference << endl;//42// 修改引用会修改原始变量reference = 100;cout << "After modifying reference:" << endl;cout << "Original variable: " << originalVariable << endl;//100cout << "Reference: " << reference << endl;//100return 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;//10cout << "num2: " << num2 << endl;//5return 0;
}

⛳️第十章 结构

C++结构体无需typedef后续定义也可以是Date date;   而c语言则是 struct Date today;

关于结构体的内容详见:【C语言】自学终极笔记第九章


⛳️第十一章 类

11.1 基本知识

  1. 类class默认是private;C语言的struct默认public
  2. 类名不能和函数名相同,但可以与形参同名。
  3. 对象(类class)=成员函数+成员变量
  4. 类的封装:类中有些成员是保护的,不能被外界直接修改(可以通过公共接口修改);另一些是公共的,提供接口供外界使用。

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();    // 派生类 重写函数alFunction(2);    // 派生类 重写函数(double)2derivedObj.additionalFunction(); // 派生类 额外函数alFunction(42.1); // 派生类 重写函数(double)42.1return 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 Object.display(42);    // Displaying value: 42return 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 functionreturn 0;
}


⛳️第十二章 构造函数

12.1 基本知识

  1. 构造函数作用:创建+初始化类对象

    析构函数作用:撤销类对象

  2. 构造函数、析构函数可以在类内和类外定义

构造函数:

  1. 可以有参数
  2. 无返回值,但可以有 "return;"
  3. 无函数类型
  4. 自动调用,格式为   类名
  5. 允许重载

析构函数:

  1. 没有参数
  2. 无函数类型
  3. 自动调用,格式为   ~类名
  4. 不能重载

注意:

  1. C++的每个类都必须要有构造函数,若用户未提供则系统提供一个默认的无参构造函数【用户提供则系统不再默认提供】
  2. 对于无参构造函数的创建
    Tdate today;//ok
    Tdate today();//error
  3. 对于静态成员变量,只能在类内声明,类外初始化

    #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 基本知识

堆:

  1. malloc/free和new/delete区别详见:C/C++面试题之语言基础篇(一)-CSDN博客
  2. malloc、free不会调用构造函数和析构函数
  3. new 对象数组 调用的构造函数只能是  默认的构造函数,没有提供则出错【不写C++会提供默认的无参构造函数,但用户自己给了则不再提供默认的无参构造函数】

拷贝构造函数:

拷贝场景一:对象可以初始化另一个对象

Tdate day1(12,3,1997);
Tdate day2=day1;//day1去初始化day2

拷贝场景二:对象需要做参数传递

void func(Tdate day){}int main()
{Tdate day1;func(day1);//传递给形参day
}

基本概念:

  1. 拷贝构造函数参数应该是Tdate(Tdate &day);而非Tdate(Tdate *day);
  2. C++提供默认拷贝构造函数(浅拷贝)
  3. 深拷贝与浅拷贝,区别详见C/C++面试题之语言基础篇(一)-CSDN博客  示例见14.3
  4. 建议在拷贝构造函数中使用 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: 42return 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 基本知识

静态数据成员:

  1. 类中声明,类外初始化(不能在任何函数内分配空间+初始化)。
  2. const 静态数据成员: 静态数据成员可以声明为 const,必须在类中声明时初始化。
    class MyClass {
    public:static const int constStaticData = 42;
    };
    

静态成员函数:

  1. 访问权限: 静态成员函数只能访问静态成员和静态函数,而不能访问非静态成员或非静态函数。

  2. this 指针: 静态成员函数没有隐含的 this 指针。

  3. 调用方式: 静态成员函数可以通过类名直接调用,而不需要通过类的实例。例如:ClassName::staticMemberFunction()

  4. 不能声明为 const 或 volatile: 静态成员函数不能被声明为 constvolatile,因为这两个关键字都与实例相关。

静态数据成员和静态成员函数的根本区别:

静态数据成员有this指针,而静态成员函数无this指针。

友元(friend关键字)

  1. 破坏封装性: 友元机制破坏了类的封装性,因为允许外部实体访问类的一切成员。

  2. 不是成员函数,友元声明可以在类中任何位置(效果都一样),定义在类外

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默认初始化为0void 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 基本知识

虚函数:

  1. 在基类中通过使用 virtual 关键字声明虚函数,virtual 关键字基类必须要用,而派生类可以省略。
    class Base {
    public:virtual void display() const {// 虚函数的实现}
    };
    
  2. 派生类可以重写基类中的虚函数,提供自己的实现【函数原型必须完全一样,区别于函数重载(参数个数、参数顺序、参数类型至少有一个不同)】。在派生类中,使用 override 关键字可以显式地表明这是对基类虚函数的重写【也可以没有】。
    class Derived : public Base {
    public:void display() const{ // 派生类对虚函数的实现//...}//显式重写则是void display() const override { // 派生类对虚函数的实现//...}
    };
    
  3. 虚函数不能是静态成员函数、内联函数: 虚函数必须是非静态成员函数。虚函数的调度机制是通过对象的虚函数表(vtable)来实现的,而静态成员函数不属于对象的实例,因此不能是虚函数。

  4. 构造函数不能是虚函数: 构造函数不能是虚函数。在对象构造的过程中,虚表还没有被构建,因此无法实现虚函数的多态性。

  5. 析构函数应该声明为虚函数: 如果类中包含虚函数,通常应该将析构函数声明为虚函数。这确保在使用基类指针指向派生类对象时,可以正确调用派生类的析构函数,避免内存泄漏。

纯虚函数:

纯虚函数本身在基类中没有具体的实现,而是在派生类中被强制要求实现。

纯虚函数的声明和定义的一般形式如下:

class AbstractBase {
public:virtual void pureVirtualFunction() const = 0;  // 纯虚函数声明virtual ~AbstractBase() {}  // 虚析构函数
};
注意:
  • 在声明纯虚函数时,在函数声明的末尾使用 = 0 表示这是一个纯虚函数
  • 要求在派生类中被强制要求实现
  • 如果一个类中包含了纯虚函数,它就成为抽象类

多态:

多态允许不同类型的对象调用同一函数或操作能够产生不同的响应。在C++中,主要通过虚函数(Virtual Function)来实现多态性。

分成运行时多态和静态多态

  1. 运行时多态【多态的主要形式,也称动态多态】:主要体现:虚函数和继承。通过使用指向基类的指针或引用,调用相同的虚函数时,根据实际对象类型来确定调用哪个版本的函数。
    int main() {Base* ptr = new Derived();  // 指向派生类对象的基类指针ptr->display();  // 调用派生类的实现,而不是基类的实现delete ptr;return 0;
    }
    
  2. 编译时多态【静态多态】:主要体现:函数重载和模板实现。在编译时确定调用哪个函数。
    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 5cout << "Area: " << shape1->area() << endl;  // 输出:Area: 78.5shape2->display();  // 输出:Rectangle with length 4 and width 6cout << "Area: " << shape2->area() << endl;  // 输出:Area: 24// 释放动态分配的内存delete shape1;delete shape2;return 0;
}


⛳️第十八章 运算符重载

18.1 基本知识

运算符重载

  1. 优先级和结合性、操作个数保持不变。
  2. 不能重载运算符:点操作(.)、域操作(::)、条件操作符(?)等等
  3. 重载形式
    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 + al, 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 基本知识

模板

  1. 分为类模板+函数模板

使用模板的优势:

  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);
    
  2. 灵活性: 模板提供了一种在编译时实现多态性的方式。通过在编译时生成特定的代码版本,可以避免运行时的性能开销,并在编译时进行类型检查。

  3. 代码重用: 使用模板可以创建通用的数据结构和算法,以适应不同的需求。这样可以减少代码的复制粘贴,提高代码的重用性。

    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, ... 是模板参数列表,用逗号分隔。这些模板参数可以在类定义中的成员变量、成员函数、构造函数等地方使用,起到泛型的作用。

函数模板:

  1. 函数模板的一般语法如下:
    template <typename T>
    T functionName(T param1, T param2, ...) {// 函数体
    }
    

    其中,typename T 表示模板参数,T 可以是任何合法的标识符,用于表示函数的参数和返回类型。在实际调用时,编译器会根据传入的参数类型,自动推导出正确的类型。

  2. 待续

函数模板和模板函数区别

  1. 函数模板: 函数模板是模板的定义。创建通用函数的机制,其中函数的定义使用模板参数。这使得函数能够接受不同类型的参数,从而实现对多种数据类型的通用操作。函数模板使用 template 关键字声明,并且可以包含一个或多个类型参数。
    template <typename T>
    T add(T a, T b) {return a + b;
    }
    
  2. 模板函数: 模板函数是函数定义。指通过函数模板实例化得到的具体函数。在调用函数时,编译器会根据传递的参数类型自动生成相应的函数版本。
    int result_int = add(3, 4);      // 实例化为 int 版本
    double result_double = add(3.5, 4.5);  // 实例化为 double 版本
    

类模板和模板类区别

  1. 类模板: 类模板是模板定义。一种创建通用类的机制,其中类的定义使用模板参数。这使得类能够处理不同类型的数据,从而实现对多种数据类型的通用数据结构或算法。类模板使用 template 关键字声明,并且可以包含一个或多个类型参数。
    template <typename T>
    class Pair {
    private:T first;T second;public:Pair(const T& f, const T& s) : first(f), second(s) {}
    };
    
  2. 模板类: 模板类是类定义。指通过类模板实例化得到的具体类。在使用类时,可以为类的模板参数指定具体的类型,从而实例化得到特定的类。
    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: " << First() << ", " << Second() << endl;cout << "Double Pair: " << First() << ", " << Second() << endl;return 0;
}
/*输出
Int Pair: 1, 2
Double Pair: 3.14, 2.71
*/


📝总结

嘘,听说有一位C++大师突破了次元壁,成功通关了编程的炼金之路!从入门到进阶,你的代码之旅如同编程宇宙的星空,熠熠生辉。你不仅掌握了代码的秘法,更诠释了编程的独特魔力。每一次Debug都是一场魔法修炼,每一行代码都是一篇炫目的咒语。恭喜你,编程冒险家,你已经成为这片代码大陆的传奇英雄。未来等着你用键盘书写更加壮丽的代码史

诗,展开属于你的数字冒险。继续释放编码魔法,让代码的光芒照亮前行的路途!

本文发布于:2024-02-02 03:25:38,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170681677941074.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:笔记
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23