目录
Qt背景及环境搭建
编辑
基础语法 数据类型
内联函数 inline
Lambda表达式
通过函数调用中加lambda匿名函数
参数捕获
Lambda和内联函数区别编辑
函数指针
Lambda匿名函数小案例
通过结构体初始化,和指针初始化结构体
c++类的引入
::是命名空间
在一个类中使用另一个类 组合
银行取钱小案例
指针和引用
函数重载和运算符重载
列表初始化的构造函数
this关键字
delect数组和指针
构造函数
什么是析构函数
静态变量
继承
分文件编程
权限对继承的影响
虚函数
多重继承
菱形继承 ---- 虚继承解决
虚继承和构造函数继承
继承相关概念表编辑
多态
如何实现多态
为什么使用多态
抽象类
Qt 是一个跨平台的应用程序和用户界面框架,用于开发图形用户界面(GUI)应用程序以及命令行工具。它最初由挪威的 Trolltech (奇趣科技)公司开发,现在由 Qt Company 维护,2020年12月8日发布QT6。Qt 使用 C++ 语言编写,支持多种编程语言通过绑定进行使用。
对于许多开发者和小型企业来说,Qt 的开源版提供了一个强大且灵活的开发框架,而对于需要额外支持和专有功能的大型企业或具有特定需求的项目,商业版则提供了所需的服务和资源。
Qt 商业版
商业版提供专有许可,需要购买许可证来使用。这适用于希望在不共享源代码的情况下开发商业软件的公司和开发人员
QT免费开源版
开源版根据 GNU Lesser General Public License (LGPL) 和 GNU General Public License (GPL) 发布。这意味着用户可以免费使用 Qt,但必须遵守特定的开源许可条款
下载windowsQT安装包
QT版本是:Index of /archive/qt/5.12/5.12.9
大家登录QT官网可能会失败,这里可以不需要QT账号,直接离线安装,所以要断开网络。
选择windows底下的编译工具,QT源代码,QT的绘图模块及QT的虚拟键盘
宽字符的用法
//在qt中不能输出 在linux中可以输出 你好,世界!
#include
#include
#include
int main() {
// 设置本地化以支持宽字符
std::setlocale(LC_ALL, "");
// 使用 wchar_t 类型定义一个宽字符串
wchar_t wstr[] = L"你好,世界!";
// 在 C++ 中打印宽字符串
std::wcout << wstr << std::endl;
return 0;
}
内联函数(Inline Function)是C++中一种特殊的函数,其定义直接在每个调用点展开。
这样可以减少函数调用的开销,尤其是在小型函数中。
特点
减少函数调用开销:内联函数通常用于优化小型、频繁调用的函数,因为它避免了函数调用的常规开销(如参数传递、栈操作等)。
编译器决策:即使函数被声明为内联,编译器也可能决定不进行内联,特别是对于复杂或递归函数。
适用于小型函数:通常只有简单的、执行时间短的函数适合做内联。
定义在每个使用点:内联函数的定义(而非仅仅是声明)必须对每个使用它的文件都可见,通常意味着将内联函数定义在头文件中。
使用方法
通过在函数声明前添加关键字 inline
来指示编译器该函数适合内联:
#include
inline int add(int a, int b) {
return a + b;
}
int main() {
int result = add(5, 3); // 编译器可能会将此替换为:int result = 5 + 3;
std::cout << "Result: " << result << std::endl;
return 0;
}
注意事项
过度使用的风险:不应滥用内联函数,因为这可能会增加最终程序的大小(代码膨胀)。对于大型函数或递归函数,内联可能导致性能下降。
编译器的决定:最终是否将函数内联是由编译器决定的,即使函数被标记为 inline。
适用场景:最适合内联的是小型函数和在性能要求高的代码中频繁调用的函数。
内联函数是一种用于优化程序性能的工具,但需要合理使用,以确保代码的可维护性和性能的平衡。
Lambda 表达式是 C++11 引入的一种匿名函数的方式,它允许你在需要函数的地方内联地定义函数,而无需单独命名函数
Lambda 表达式的基本语法如下:
[capture clause](parameters) -> return_type {
// 函数体
// 可以使用捕获列表中的变量
return expression; // 可选的返回语句
}
Lambda 表达式由以下部分组成:
捕获列表(Capture clause):用于捕获外部变量,在 Lambda 表达式中可以访问这些变量。捕获列表可以为空,也可以包含变量列表 [var1, var2, ...]。
参数列表(Parameters):与普通函数的参数列表类似,可以为空或包含参数列表 (param1, param2, ...)。
返回类型(Return type):Lambda 表达式可以自动推断返回类型,也可以显式指定返回类型 -> return_type。如果函数体只有一条返回语句,可以省略返回类型。
函数体(Body):Lambda 表达式的函数体,包含需要执行的代码。
Lambda 表达式最简单的案例是在需要一个小型函数或临时函数时直接使用它。以下是一个非常简单的例子,其中使用 Lambda 表达式来定义一个加法操作,并立即使用它来计算两个数的和。一般用auto占位 符表示函数返回类型 最后需要加分号
示例:使用 Lambda 表达式进行加法
int main() {
// 定义一个整数变量x,赋值为10
int x = 10;
// 定义一个整数变量y,赋值为20
int y = 20;
// 定义一个lambda函数 要用auto来,接受两个整数参数x和y,返回它们的和
// auto用于声明add函数的类型 占位符,防止类型出错
// lambda 匿名函数最后要加分号
auto add = [](int x, int y) -> int {
return x + y;
};
// 调用add函数,传入x和y的值,将结果赋值给整数变量num
int num = add(x, y);
// 输出num的值
cout << num << endl;
// 程序正常结束,返回0
return 0;
}
#include
using namespace std;
// 参数a 参数b 函数指针(指针Compore函数的地址 其中的ab只是格式)
int getMax(int a,int b, bool (*p)(int ,int )){
if(p(a,b)){ // if里面的返回值是真则成立
return a;
}else{
return b;
}
}
int main()
{
int a = 50;
int b = 20;
// 函数调用中使用lambda 匿名函数
int max = getMax(a,b,[](int a,int b) -> bool{
return a>b;
});
cout << max << endl;
return 0;
}
是指 Lambda 表达式从其定义的上下文中捕获变量的能力。这使得 Lambda 可以使用并操作在其外部定义的变量。捕获可以按值(拷贝)或按引用进行。
第一个 Lambda 表达式 sum 按值捕获了 x 和 y(即它们的副本)。这意味着 sum 内的 x 和 y 是在 Lambda 定义时的值的拷贝。
第二个 Lambda 表达式使用 [=] 捕获列表,这表示它按值捕获所有外部变量。
第三个 Lambda 表达式 使用 [&] 捕获列表,这表示它按引用捕获所有外部变量。因此,它可以修改 x 和 y 的原始值。
按值捕获是安全的,但不允许修改原始变量,而按引用捕获允许修改原始变量,但需要注意引用的有效性和生命周期问题。
#include
using namespace std;
int main()
{
int a = 10;
int b = 20;
//可以捕获ab的值 只有读权限
auto add = [a,b]()->int{
return a+b;
};
int ret1 = add();
cout << "在捕获ab时ret1:" << ret1 << endl;
int c = 30;
//使用 = 可以捕获所有值 只有读权限
auto add2 = [=]()->int{
// c = 50; 错误
return a+b+c;
};
int ret2 = add2();
cout << "在捕获abc时ret2:" << ret2 << endl;
//使用& 引用方式时 可以修改变量的值
auto mul = [&]()->int{
c = 50;
return a*b*c;
};
int ret3 = mul();
cout << "在捕获引用时:" << ret3 << endl;
cout << "c = " << c << endl;
cout << "Hello World!" << endl;
return 0;
}
#include
using namespace std;
// 如果a > b返回为true真 否则返回为假false
bool Compore(int a,int b){
return a > b;
}
// 参数a 参数b 函数指针(指针Compore函数的地址 其中的ab只是格式)
int getMax(int a,int b, bool (*p)(int ,int )){
if(p(a,b)){ // if里面的返回值是真则成立
return a;
}else{
return b;
}
}
int main()
{
int a = 10;
int b = 20;
// 传的函数名就是地址,要用函数指针接收
int ret = getMax(a,b,Compore);
cout << ret << endl;
return 0;
}
int main()
{
double a;
double b;
char temp;
//lambda 匿名函数 auto类型 指向匿名函数 []捕获()参数->类型
auto add = [](double a,double b)->double{cout << "a+b = " << a+b << endl; return a+b;};
auto sub = [](double a,double b)->double{cout << "a-b = " << a-b << endl;return a-b;};
auto mul = [](double a,double b)->double{cout << "a*b = " << a*b << endl;return a*b;};
auto divl = [](double a,double b)->double{cout << "a/b = " << a/b << endl;return a/b;};
while(1){
cout << "请输入a,b的值" << endl;
cin >> a;
cin >> b;
cout << "请输入计算表达式 + - * /" << endl;
cin >> temp;
switch(temp){
case '+':add(a,b);
break;
case '-':sub(a,b);
break;
case '*':mul(a,b);
break;
case '/':divl(a,b);
break;
}
}
return 0;
}
//回调函数 传入参数a,b和一个函数指针 函数指针指向匿名函数 把ab传进去 在匿名函数中处理
void callback(double a,double b,double (*pFunction)(double ,double )){
cout << "开始计算..." << endl;
pFunction(a,b);
}
int main()
{
double a;
double b;
char temp;
// auto add = [](double a,double b)->double{cout << "a+b = " << a+b << endl; return a+b;};
// auto sub = [](double a,double b)->double{cout << "a-b = " << a-b << endl;return a-b;};
// auto mul = [](double a,double b)->double{cout << "a*b = " << a*b << endl;return a*b;};
// auto divl = [](double a,double b)->double{cout << "a/b = " << a/b << endl;return a/b;};
while(1){
cout << "请输入a,b的值" << endl;
cin >> a;
cin >> b;
cout << "请输入计算表达式 + - * /" << endl;
cin >> temp;
switch(temp){
case '+':callback(a,b,[](double a,double b)->double{cout << "a+b = " << a+b << endl; return a+b;});
break;
case '-':callback(a,b,[](double a,double b)->double{cout << "a-b = " << a-b << endl; return a-b;});
break;
case '*':callback(a,b,[](double a,double b)->double{cout << "a*b = " << a*b << endl; return a*b;});
break;
case '/':callback(a,b,[](double a,double b)->double{cout << "a/b = " << a/b << endl; return a/b;});
break;
}
}
return 0;
}
#include
#include
struct Car{
char* color;//颜色
char* brand;//品牌
char* type; //车型
int year; //年限
//函数指针
void (*PrintfCarInfo)(char* color,char* brand,char* type,int year);// void (*pCarInfo)(char* ,char* ,char* ,int );
void (*RunCar)(char* type);
void (*StopCar)(char* type);
};
void BWMInfo(char* color,char* brand,char* type,int year){
printf("车的品牌是:%s, 颜色是:%s, 车型是:%s, 年限是:%d\n",brand,color,type,year);
}
void AodiA6Info(char* color,char* brand,char* type,int year){
printf("车的品牌是:%s, 颜色是:%s, 车型是:%s, 年限是:%d\n",brand,color,type,year);
}
int main()
{
struct Car BWMThree;
BWMThree.color = "白色";
BWMThree.brand = "宝马";
BWMThree.type = "3系";
BWMThree.year = 2022;
BWMThree.PrintfCarInfo = BWMInfo;//指针指向这个函数 然后传递参数过去
BWMThree.PrintfCarInfo(BWMThree.color,BWMThree.brand,BWMThree.type,BWMThree.year);
struct Car* AodiA6;
AodiA6 = (struct Car*)malloc(sizeof(struct Car));
AodiA6->color = "黑色";
AodiA6->brand = "奥迪";
AodiA6->type = "A6";
AodiA6->year = 2008;
AodiA6->PrintfCarInfo = AodiA6Info;//指针指向这个函数 然后传递参数
AodiA6->PrintfCarInfo(AodiA6->color,AodiA6->brand,AodiA6->type,AodiA6->year);
return 0;
}
如果是指针指向的类需要用new Car开实例化一个对象
#include
#include
#include
#include
using namespace std;
class Car{
public:
string color;//颜色
string brand;//品牌
string type; //车型
int year; //年限
//函数指针
void (*PrintfCarInfo)(string color,string brand,string type,int year);// void (*pCarInfo)(char* ,char* ,char* ,int );
void (*RunCar)(string type);
void (*StopCar)(string type);
};
void BWMInfo(string color,string brand,string type,int year){
// printf("车的品牌是:%s, 颜色是:%s, 车型是:%s, 年限是:%d\n",brand,color,type,year);
string str = "车的品牌是:" + brand + " 颜色是:" + color + " 车型是:" + type + " 年限是:" + to_string(year);
cout << str << endl;
//cout << brand << color << type << year << endl;
}
void AodiA6Info(string color,string brand,string type,int year){
// printf("车的品牌是:%s, 颜色是:%s, 车型是:%s, 年限是:%d\n",brand,color,type,year);
cout << brand << color << type << year << endl;
}
int main()
{
Car BWMThree;
BWMThree.color = "白色";
BWMThree.brand = "宝马";
BWMThree.type = "3系";
BWMThree.year = 2022;
BWMThree.PrintfCarInfo = BWMInfo;//指针指向这个函数 然后传递参数过去
BWMThree.PrintfCarInfo(BWMThree.color,BWMThree.brand,BWMThree.type,BWMThree.year);
Car* AodiA6 = new Car;//指针指向实例话的类 不用malloc开辟空间
//AodiA6 = (struct Car*)malloc(sizeof(struct Car));
cout << "调试1" << endl;
AodiA6->color = "黑色";
AodiA6->brand = "奥迪";
AodiA6->type = "A6";
AodiA6->year = 2008;
AodiA6->PrintfCarInfo = AodiA6Info;//指针指向这个函数 然后传递参数
cout << "调试2" << endl;
AodiA6->PrintfCarInfo(AodiA6->color,AodiA6->brand,AodiA6->type,AodiA6->year);
return 0;
}
#include
#include
#include
#include
using namespace std;
class Car{
public://公有的
//成员属性
string color;//颜色
string brand;//品牌
string type; //车型
int year; //年限
//函数指针
void (*PrintfCarInfo)(string color,string brand,string type,int year);// void (*pCarInfo)(char* ,char* ,char* ,int );
void (*RunCar)(string type);
void (*StopCar)(string type);
//成员方法 声明
void PrintfCarMessage();
};
// void Car:: ::是命名空间,即printfCarMessage是Car中的方法 -- 函数
void Car::PrintfCarMessage(){
cout << "这是成员方法 函数" << endl;
string str = "车的品牌是:" + brand + " 颜色是:" + color + " 车型是:" + type + " 年限是:" + to_string(year);
cout << str << endl;
}
void BWMInfo(string color,string brand,string type,int year){
string str = "车的品牌是:" + brand + " 颜色是:" + color + " 车型是:" + type + " 年限是:" + to_string(year);
cout << str << endl;
}
void AodiA6Info(string color,string brand,string type,int year){
string str = "车的品牌是:" + brand + " 颜色是:" + color + " 车型是:" + type + " 年限是:" + to_string(year);
cout << str << endl;
}
int main()
{
Car BWMThree;
BWMThree.color = "白色";
BWMThree.brand = "宝马";
BWMThree.type = "3系";
BWMThree.year = 2022;
// BWMThree.PrintfCarInfo = BWMInfo;//指针指向这个函数 然后传递参数过去
// BWMThree.PrintfCarInfo(BWMThree.color,BWMThree.brand,BWMThree.type,BWMThree.year);
BWMThree.PrintfCarMessage();
Car* AodiA6 = new Car;//指针指向实例话的类 不用malloc开辟空间
//AodiA6 = (struct Car*)malloc(sizeof(struct Car));
AodiA6->color = "黑色";
AodiA6->brand = "奥迪";
AodiA6->type = "A6";
AodiA6->year = 2008;
// AodiA6->PrintfCarInfo = AodiA6Info;//指针指向这个函数 然后传递参数
// AodiA6->PrintfCarInfo(AodiA6->color,AodiA6->brand,AodiA6->type,AodiA6->year);
AodiA6->PrintfCarMessage();
return 0;
}
#include
#include
#include
#include
using namespace std;
// 再一个类中包含另一个类 叫作组合,如果是指针指向的类需要用new实例化开辟空间
class whell{
public:
string brand;//轮胎品牌
int year;
// 方法声明
void PrintfWhll();
};
//::是作用空间 说明printfwhell是whell类中的方法
void whell::PrintfWhll(){
cout << "轮胎的品牌是:" << brand << endl;
cout << "轮胎的日期是:" << year << endl;
}
class Car{
public://公有的
//成员属性
string color;//颜色
string brand;//品牌
string type; //车型
int year; //年限
void (*PrintfCarInfo)(string color,string brand,string type,int year);// void (*pCarInfo)(char* ,char* ,char* ,int ); //函数指针
void (*RunCar)(string type);
void (*StopCar)(string type);
//成员方法 声明
void PrintfCarMessage();
//在Car类中使用另一个类
// whell lunTai;
//通过指针的方式在车类中实例化另一个类
whell* lunTai = new whell;
};
// void Car:: ::是命名空间,即printfCarMessage是Car中的方法 -- 函数
void Car::PrintfCarMessage(){
cout << "这是成员方法 函数" << endl;
string str = "车的品牌是:" + brand + " 颜色是:" + color + " 车型是:" + type + " 年限是:" + to_string(year);
cout << str << endl;
}
void BWMInfo(string color,string brand,string type,int year){
string str = "车的品牌是:" + brand + " 颜色是:" + color + " 车型是:" + type + " 年限是:" + to_string(year);
cout << str << endl;
}
void AodiA6Info(string color,string brand,string type,int year){
string str = "车的品牌是:" + brand + " 颜色是:" + color + " 车型是:" + type + " 年限是:" + to_string(year);
cout << str << endl;
}
int main()
{
Car BWMThree;
BWMThree.color = "白色";
BWMThree.brand = "宝马";
BWMThree.type = "3系";
BWMThree.year = 2022;
// BWMThree.PrintfCarInfo = BWMInfo;//指针指向这个函数 然后传递参数过去
// BWMThree.PrintfCarInfo(BWMThree.color,BWMThree.brand,BWMThree.type,BWMThree.year);
BWMThree.PrintfCarMessage();
// 在车类中赋值轮胎类的属性,通过方法打印
// BWMThree.lunTai.brand = "米其林";
// BWMThree.lunTai.year = 2021;
// BWMThree.lunTai.PrintfWhll();
//指针指针的轮胎
BWMThree.lunTai->brand = "米其林轮胎";
BWMThree.lunTai->year = 2024;
BWMThree.lunTai->PrintfWhll();
Car* AodiA6 = new Car;//指针指向实例话的类 不用malloc开辟空间
//AodiA6 = (struct Car*)malloc(sizeof(struct Car));
AodiA6->color = "黑色";
AodiA6->brand = "奥迪";
AodiA6->type = "A6";
AodiA6->year = 2008;
// AodiA6->PrintfCarInfo = AodiA6Info;//指针指向这个函数 然后传递参数
// AodiA6->PrintfCarInfo(AodiA6->color,AodiA6->brand,AodiA6->type,AodiA6->year);
AodiA6->PrintfCarMessage();
// 通过类在车类赋值类中轮胎类的属性。
// AodiA6->lunTai.brand = "正兴轮胎";
// AodiA6->lunTai.year = 2006;
// AodiA6->lunTai.PrintfWhll();
//指针指向的轮胎
AodiA6->lunTai->brand = "正兴轮胎";
AodiA6->lunTai->year = 2005;
AodiA6->lunTai->PrintfWhll();
return 0;
}
#include
using namespace std;//命名空间
class bank{
//私有属性
private:
string name; //姓名
string iphone; //电话
string addr; //地址
double balance;//余额
//公有方法
public:
void manMessage(string newName,string newIphone,string newAddr,double newBlalance);// 银行登录信息 流程
void getMoney(double amount);//取钱
void putMoney(double amount);//存钱
void seeMoney();//查看余额
void printfUserInfo();//打印信息
};
//
void bank::manMessage(string newName, string newIphone, string newAddr, double newBlalance){
name = newName;
iphone= newIphone;
addr = newAddr;
balance = newBlalance;
}
void bank::getMoney(double amount){
if(balance <= 0){
cerr << "你没钱" << endl;
}else if(amount > balance){
cerr << "无法取出 余额不足 仅剩:" << balance << endl;
}else{
balance -= amount;
cout << "已经成功取款:" << amount << "元" << "当前余额为:" << balance << "元" << endl;
}
}
void bank::putMoney(double amount){
balance += amount;
cout << "存款成功,当前余额为:" << balance << "元" << endl;
}
void bank::seeMoney(){
cout << "当前余额:" << balance << "元" << endl;
}
void bank::printfUserInfo(){
string userInfo = "姓名:" + name + " 电话:" + iphone + " 地址:" + addr + " 余额:" + to_string(balance);
cout << userInfo << endl;
}
int main()
{
class bank b1;
b1.manMessage("张三","10086","厦门",100);
b1.getMoney(50);
b1.putMoney(200);
b1.seeMoney();
b1.printfUserInfo();
return 0;
}
指针和引用的区别:
// 引用就是换个别名且不占用内存,和原来是同一个东西,引用初始化后不能改变,不存在空引用
// 而指针是变量,占用空间存储的是一个地址,初始化后可以改变指向,指针可以指向空
作用:引用可以做函数的返回值存在
注意:不要返回局部变量的引用
用法:用函数作为左值------返回一个引用,用int& ret接收,也可以通过赋值改变
//指针和引用
#include
using namespace std;
void swap01(int a,int b){
int temp;
temp = a;
a = b;
b = temp;
}
void swap02(int* pa,int* pb){
int temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}
//引用
void swap03(int& ra,int& rb){
int temp;
temp = ra;
ra = rb;
rb = temp;
}
int main()
{
int a = 10;
int b = 20;
cout << "交换前:" << a << "," << b << endl;
swap01(a,b);
cout << "swap01交换后:" << a << "," << b << endl;
// swap02(&a,&b);
// cout << "swap02交换后:" << a << "," << b << endl;
swap03(a,b);
cout << "swap03交换后:" << a << "," << b << endl;
return 0;
}
// 1.0通过引用做左值
// 2.0不要通过引用返回堆栈中的地址
#include
using namespace std;
int arry[] = {10,20,30,40,50,60};
//通过返回引用修改数组中的值
int& modifiction(int* arry){
int& w_arry3 = arry[3];
return w_arry3;
}
int& modifiction01(int* arry){
int a = 10;
int& w_arry3 = a;
return w_arry3; //警告!! 不能通过引用返回栈中的值 因为调用完已经释放该空间
}
int main()
{
cout << modifiction(arry) << endl;//通过引用返回w_arry3 是全局变量数组3的值
modifiction(arry) = 80;// 引用可以做左值
cout << arry[3] << endl;
modifiction01(arry);
return 0;
}
函数重载
3.3.1 函数重载概述
作用: 函数名可以相同,提高复用性
函数重载满足条件:
同一个作用域下 函数名称相同
函数参数类型不同 或者 个数不同 或者 顺序不同
注意 : 函数的返回值不可以作为函数重载的条件
//函数重载 1.类型不同 2.参数个数不同 3.顺序不同 4.以实参为准 5.形参必须从左往右有初始值
#include
using namespace std;
class num{
public:
void intNum(int w_i);
void doubleNum(double w_d);
void stringNum(string w_s);
void demo01();
};
void num::intNum(int w_i){
cout << "这是整数:" << w_i << endl;
}
void num::doubleNum(double w_d){
cout << "这是小数:" << w_d << endl;
}
void num::stringNum(string w_s){
cout << "这是字符串:" << w_s << endl;
}
void num::demo01(){
cout << "这是无参数demo01" << endl;
}
void demo02(int a){
cout << "这是单个参数demo02" << endl;
}
void demo02(int a,double b){
cout << "这是两个参数demo02" << endl;
}
//void demo02(int a,double b){
// cout << "这是两个参数demo02" << endl;
//}
void demo02(string str){
cout << "这是字符串参数demo02" << endl;
}
int main()
{
num n1;
n1.intNum(5);
n1.doubleNum(18.8);
n1.stringNum("hello");
n1.demo01();
demo02(100);
demo02(15,22.2);
demo02("yanglagn");
return 0;
}
#include
using namespace std;
class person{
public:
string name;
int age;
bool eqPerson(person p);
};
bool person::eqPerson(person p){
return this->name == p.name && this->age == p.age;
}
int main()
{
person p1;
p1.name = "张三";
p1.age = 18;
person p2;
p2.name = "张三";
p2.age = 18;
bool p3 = (p1.eqPerson(p2));
cout << p3 << endl;
return 0;
}
#include
using namespace std;
class car{
public:
string name;
int age;
//通过构造函数列表初始化 //传过来的参数赋值给name和age
car(string w_name,int w_age):name(w_name),age(w_age){
cout << "列表初始化构造函数" << endl;
}
car(){
cout << "这是默认构造函数" << endl;
}
~car(){
cout << "这是析构函数" << endl;
}
void printfInfo(){
cout << "品牌:" << name << " 年限:" << age << endl;
}
};
int main()
{
car c1;
car c2("别克",2021);
c2.printfInfo();
return 0;
}
this
关键字是一个指向调用对象的指针。它在成员函数内部使用,用于引用调用该函数的对象。使用this
可以明确指出成员函数正在操作的是哪个对象的数据成员。下面是一个使用Car
类来展示this
关键字用法的示例:c++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象
this 指针是隐含每一个非静态成员函数内的一种指针
this 指针不需要定义,直接使用即可
this 指针的用途:
当形参和成员变量同名时,可用this指针来区分
在类的非静态成员函数中返回对象本身,可使用return *this
#include
using namespace std;
class car{
public:
string name;
int age;
//this当前对象所指向的地址 可以修改值
//列表初始化构造函数
car(string w_name,int w_age):name(w_name),age(w_age){
cout << "这是有参构造函数" << endl;
cout << "这是this指向的地址:" << this << endl;
}
~car(){
cout << "这是析构函数" << endl;
}
//把newAge赋值给this指向对象的age后 把this作为引用对象返回 返回的是car对象
car& setAge(int newAge){
this->age = newAge;
return *this;
}
void display(){
cout << "品牌:" << name << " 年份:" << age << endl;
}
};
int main()
{
car c1("宾利",2024);
c1.display();
cout << "这是在main中c1的地址:" << &c1 << endl;
//以引用方式返回this 并通过链式调用
car c2("宝马",2005);
//通过c2.setAge设置年限返回car对象 再调用car的dispaly显示
c2.setAge(2015).display();
return 0;
}
在这个例子中,Car 类的构造函数使用 this 指针来区分成员变量和构造函数参数。同样,setYear 成员函数使用 this 指针来返回调用该函数的对象的引用,这允许链式调用,如 myCar.setYear(2021).display();。在 main 函数中创建了 Car 类型的对象,并展示了如何使用这些成员函数。
#include
using namespace std;
class car{
public:
string name;
int age;
};
int main()
{
car* c1 = new car;
delete c1;
int arry[] = {10,20,30,40,50};
int* pArry = new int[sizeof(arry)/sizeof(arry[0])];
pArry = arry;
cout << arry << endl;
cout << pArry << endl;
for(int i = 0;i < 5;i++){
cout << pArry[i] << endl;
}
delete[] pArry;
return 0;
}
什么是析构函数
析构函数是C++中的一个特殊的成员函数,它在对象生命周期结束时被自动调用,用于执行对象销毁前的清理工作。析构函数特别重要,尤其是在涉及动态分配的资源(如内存、文件句柄、网络连接等)的情况下。
构造函数调用规则
默认情况下, c++ 编译器至少给一个类添加 3 个函数
1 .默认构造函数 ( 无参,函数体为空 )
2 .默认析构函数 ( 无参,函数体为空 )
3 .默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
如果用户定义有参构造函数, c++ 不在提供默认无参构造,但是会提供默认拷贝构造
如果用户定义拷贝构造函数, c++ 不会再提供其他构造函数
//使用new关键字动态分配内存时,需要为对象提供一个构造函数的参数列表来初始化对象
//开辟空间要用delect来释放,有delect才能调用析构函数
#include
using namespace std;
class demo{
private:
int datas;
public:
demo(int m_datas):datas(m_datas){
cout << "这是有参构造函数 dtas = " << datas << endl;//有参构造函数通过列表初始化
}
~demo(){
cout << "这是析构函数" << endl;
}
};
int main()
{
demo d1(100);
//使用new关键字动态分配内存时,需要为对象提供一个构造函数的参数列表来初始化对象
//开辟空间要用delect来释放,有delect才能调用析构函数
demo* d2 = new demo(50);
delete d2;
return 0;
}
静态成员变量
定义:静态成员变量是类的所有对象共享的变量。与普通成员变量相比,无论创建了多少个类的实例,静态成员变量只有一份拷贝。
初始化:静态成员变量需要在类外进行初始化,通常在类的实现文件中。
访问:静态成员变量可以通过类名直接访问,不需要创建类的对象。也可以通过类的对象访问。
用途:常用于存储类级别的信息(例如,计数类的实例数量)或全局数据需要被类的所有实例共享。
静态成员函数定义:静态成员函数是可以不依赖于类的实例而被调用的函数。它不能访问类的非静态成员变量和非静态成员函数。
访问:类似于静态成员变量,静态成员函数可以通过类名直接调用,也可以通过类的实例调用。
用途:常用于实现与具体对象无关的功能,或访问静态成员变量。
存在的意义
- 共享数据:允许对象之间共享数据,而不需要每个对象都有一份拷贝。
- 节省内存:对于频繁使用的类,使用静态成员可以节省内存。
- 独立于对象的功能:静态成员函数提供了一种在不创建对象的情况下执行操作的方法,这对于实现工具函数或管理类级别状态很有用。
//一个类中访问另一个类中的静态变量
#include
using namespace std;
class school{
public:
string room;
static int studentNum; //静态变量 类外初始化,通过静态函数访问
static void seeStudent(){
cout << "这是静态函数中访问静态变量:" << studentNum << endl;//静态函数不能直接访问非静态变量,非静态的可以访问静态函数,因为现有静态函数
}
// 通过普通函数去修改静态变量
void countStudentNum(){
studentNum++;
}
};
int school::studentNum = 10;
class home{
public:
void printfInfoStudent(){
cout << "这是home中查看school中的静态变量 :" << school::studentNum << endl;
}
void countStudentNum(){
school::studentNum++;
}
};
int main()
{
school s1;
s1.countStudentNum();
s1.seeStudent();
//在home中调用school的属性时要加上命名空间 school::
home h1;
h1.countStudentNum();
h1.printfInfoStudent();
return 0;
}
继承基本概念
继承是面向对象编程(OOP)中的一个核心概念,特别是在C++中。它允许一个类(称为派生类或子类)继承另一个类(称为基类或父类)的属性和方法。继承的主要目的是实现代码重用,以及建立一种类型之间的层次关系。特点
代码重用:子类继承了父类的属性和方法,减少了代码的重复编写。
扩展性:子类可以扩展父类的功能,添加新的属性和方法,或者重写(覆盖)现有的方法。
多态性:通过继承和虚函数,C++支持多态,允许在运行时决定调用哪个函数。
基本用法在C++中,继承可以是公有(public)、保护(protected)或私有(private)的,这决定了基类成员在派生类中的访问权限
注意父类和子类都要加上权限
#include
using namespace std;
//基类 父类 交通工具
class Vehicle{
public:
string type; //类型
string contry; //国家
string color; //颜色
double mony; //价格
int lunTai; //轮子
void run(){
cout << "开车" << endl;
}
void stop(){
cout << "停车" << endl;
}
};
//派生类 子类 公交车继承交通工具
class Bus:public Vehicle{
public:
void upUser(){
cout << "公交站上车!" << endl;
}
void downUser(){
cout << "到站下车!" << endl;
}
//打印信息
void printfUserVehicle(){
cout << type << contry << color << mony << lunTai << endl;
}
};
int main()
{
//公交车继承交通工具 可以重写,可以增加方法
Bus myBus;
myBus.type = "公交车";
myBus.contry = "中国";
myBus.color = "白色";
myBus.mony = 400000;
myBus.lunTai = 8;
myBus.printfUserVehicle();
myBus.run();
myBus.upUser();
myBus.downUser();
myBus.stop();
return 0;
}
1.创建工程main 2.创建父类 3.创建子类---继承父类
在C++中,访问控制符对继承的影响可以通过下表来清晰地展示。这个表格展示了不同类型的继承(
public
、protected
、private
)如何影响基类的不同类型成员(public
、protected
、private
)在派生类中的访问级别。
优先考虑父类权限,再考虑子类权限
父类的protected子类可以访问 即类内可以访问,但是不能通过子类的对象访问, 父类的privite则子类和类的对象都不能访问
解释:
子类public 继承:基类的 public 成员在派生类中仍然是 public 的,protected 成员仍然是 protected 的。基类的 private 成员在派生类中不可访问。
protected 继承:基类的 public 和 protected 成员在派生类中都变成 protected 的。基类的 private 成员在派生类中不可访问。
private 继承:基类的 public 和 protected 成员在派生类中都变成 private 的。基类的 private 成员在派生类中不可访问。
这个表格提供了一个快速参考,帮助理解在不同类型的继承中基类成员的访问级别是如何变化的。记住,无论继承类型如何,基类的 private 成员始终不可直接在派生类中访问。
在C++中,virtual 和 override 关键字用于支持多态,尤其是在涉及类继承和方法重写的情况下。正确地理解和使用这两个关键字对于编写可维护和易于理解的面向对象代码至关重要。
virtual 关键字
使用场景:在基类中声明虚函数。
目的:允许派生类重写该函数,实现多态。
行为:当通过基类的指针或引用调用一个虚函数时,调用的是对象实际类型的函数版本。
示例:override 关键字
- 使用场景:在派生类中重写虚函数。
- 目的:明确指示函数意图重写基类的虚函数。
- 行为:确保派生类的函数确实重写了基类中的一个虚函数。如果没有匹配的虚函数,编译器会报错。
- 示例:
class Base {
public:
virtual void func() {
std::cout << "Function in Base" << std::endl;
}
};
class Derived : public Base {
public:
void func() override {
std::cout << "Function in Derived" << std::endl;
}
};
注意点
只在派生类中使用 override:override 应仅用于派生类中重写基类的虚函数。
虚析构函数:如果类中有虚函数,通常应该将析构函数也声明为虚的。
默认情况下,成员函数不是虚的:在C++中,成员函数默认不是虚函数。只有显式地使用 virtual 关键字才会成为虚函数。
继承中的虚函数:一旦在基类中声明为虚函数,该函数在所有派生类中自动成为虚函数,无论是否使用 virtual 关键字。
正确使用 virtual 和 override 关键字有助于清晰地表达程序员的意图,并利用编译器检查来避免常见的错误,如签名不匹配导致的非预期的函数重写
多重继承
在C++中,多重继承是一种允许一个类同时继承多个基类的特性。这意味着派生类可以继承多个基类的属性和方法。多重继承增加了语言的灵活性,但同时也引入了额外的复杂性,特别是当多个基类具有相同的成员时。基本概念
在多重继承中,派生类继承了所有基类的特性。这包括成员变量和成员函数。如果不同的基类有相同名称的成员,则必须明确指出所引用的是哪个基类的成员。
//boyClass 类多重继承 继承A类B类 用逗号隔开
class boyClass:public classA,public classB
#include
using namespace std;
class classA{
public:
void printfClassA(){
cout << "this is classA" << endl;
}
void printfDemo(){
cout << "this is printfDemo" << endl;
}
};
class classB{
public:
void printfClassB(){
cout << "this is classB" << endl;
}
void printfDemo(){
cout << "this is printfDemo" << endl;
}
};
//boyClass 类多重继承 继承A类B类 用逗号隔开
class boyClass:public classA,public classB{
public:
void printfAB(){
printfClassA();
printfClassB();
classA::printfDemo(); //如果AB都有相同一个和函数名 可以在这里声明调用哪一个函数
}
};
int main()
{
boyClass boy1;
boy1.printfClassA();//boy直接中调用父类的方法
boy1.printfClassB();//boy直接调用父类方法
boy1.printfAB(); //在子类中调用两个父类的方法
return 0;
}
虚继承
虚继承是C++中一种特殊的继承方式,主要用来解决多重继承中的菱形继承问题。在菱形继承结构中,一个类继承自两个具有共同基类的类时,会导致共同基类的成员在派生类中存在两份拷贝,这不仅会导致资源浪费,还可能引起数据不一致的问题。虚继承通过确保共同基类的单一实例存在于继承层次中,来解决这一问题。特点和注意事项
初始化虚基类:在使用虚继承时,虚基类(如上例中的 Base 类)只能由最派生的类(如 FinalDerived)初始化。
内存布局:虚继承可能会改变类的内存布局,通常会增加额外的开销,比如虚基类指针。
设计考虑:虚继承应谨慎使用,因为它增加了复杂性。在实际应用中,如果可以通过其他设计(如组合或接口)避免菱形继承,那通常是更好的选择。
虚继承是C++语言中处理复杂继承关系的一种重要机制,但它也带来了一定的复杂性和性能考虑。正确地使用虚继承可以帮助你建立清晰、有效的类层次结构。
// 2.加virtual 只继承一次相同部分,拷贝到另一个区域
//通过使用虚继承,我们可以确保派生类只继承一个基类的相同成员,而不是多次继承相同的成员。这样可以避免二义性和重复成员的问题。
//boy1Base和boy2Base类都以虚方式继承了base类,这意味着它们只会继承base类的一个副本,
//而不是base类的所有副本。这样就避免了菱形继承问题,确保了派生类中只有一个data成员。
class Base {
public:
int data;
};
class Derived1 : virtual public Base {
// 虚继承 Base
};
class Derived2 : virtual public Base {
// 虚继承 Base
};
class FinalDerived : public Derived1, public Derived2 {
// 继承自 Derived1 和 Derived2
};
多态的基本概念(polymorphic)
想象一下,你有一个遥控器(这就像是一个基类的指针),这个遥控器可以控制不同的电子设备(这些设备就像是派生类)。无论是电视、音响还是灯光,遥控器上的“开/关”按钮(这个按钮就像是一个虚函数)都能控制它们,但具体的操作(打开电视、播放音乐、开灯)则取决于你指向的设备。
如何实现多态
1.基类中使用虚函数(Virtual Function):我们在基类中定义一个虚函数,这个函数可以在任何派生类中被“重写”或者说“定制”。
使用关键字 virtual 来声明。
2.创建派生类并重写虚函数:在派生类中,我们提供该虚函数的具体实现。这就像是告诉遥控器,“当你控制我的这个设备时,这个按钮应该这样工作”。
通过基类的引用或指针调用虚函数:
class remoteControl{
public:
//创建一个虚函数 相当于接口模板,给子类重写使用
virtual void open(){
}
};
// tv继承遥控器
class TvControl:public remoteControl{
public:
// 重写虚函数
void open() override{
cout << "打开电视" << endl;
}
};
为什么使用多态
- 灵活性:允许我们编写可以处理不确定类型的对象的代码。
- 可扩展性:我们可以添加新的派生类而不必修改使用基类引用或指针的代码。
- 接口与实现分离:我们可以设计一个稳定的接口,而将具体的实现留给派生类去处理。
通过父类的指针或引用 指向子类,在子类中重写虚函数
#include
using namespace std;
//父类 遥控器
class remoteControl{
public:
//创建一个虚函数 相当于接口模板,给子类重写使用
virtual void open(){
}
};
// tv继承遥控器
class TvControl:public remoteControl{
public:
// 重写虚函数
void open() override{
cout << "打开电视" << endl;
}
};
// 空调继承遥控器
class AirControl:public remoteControl{
public:
// 重写虚函数
void open() override{
cout << "打开空调" << endl;
}
};
// 灯继承遥控器
class LightControl:public remoteControl{
public:
// 重写虚函数
void open() override{
cout << "打开电灯" << endl;
}
};
void test(TvControl& r){
r.open();
}
int main()
{
//多态 向下兼容,父类引用或指针指向子类,通过重写虚函数,实现具体业务
remoteControl* c1 = new TvControl;
c1->open();
remoteControl* c2 = new AirControl;
c2->open();
remoteControl* c3 = new LightControl;
c3->open();
//直接实例化子类
TvControl tv;
tv.open();
//通过把tv传过去 用引用接收,再open
test(tv);
return 0;
}
抽象类的基本概念
是一个抽象的概念,因为它本身并不能直接被使用。你需要一个具体的交通工具,比如“汽车”或“自行车”,它们根据“交通工具”的概念具体实现了移动的功能。
在 C++ 中,抽象类就像是这样的一个抽象概念。它定义了一组方法(比如移动),但这些方法可能没有具体的实现。这意味着,抽象类定义了派生类应该具有的功能,但不完全实现这些功能。
抽象类的特点
包含至少一个纯虚函数:
抽象类至少有一个纯虚函数。这是一种特殊的虚函数,在抽象类中没有具体实现,而是留给派生类去实现。
纯虚函数的声明方式是在函数声明的末尾加上 = 0。
不能直接实例化:
由于抽象类不完整,所以不能直接创建它的对象。就像你不能直接使用“交通工具”的概念去任何地方,你需要一个具体的交通工具。
用于提供基础结构:
抽象类的主要目的是为派生类提供一个共同的基础结构,确保所有派生类都有一致的接口和行为class teacher{ public: string name; //姓名 string subject;//学科 string school; //学校 //纯虚函数 virtual void goToClass() = 0; //上课 virtual void statusToClass() = 0; //状态 virtual void stopToClass() = 0; //下课 }; //main 中不能直接实例化该父类
#include
using namespace std;
class teacher{
public:
string name; //姓名
string subject;//学科
string school; //学校
//纯虚函数
virtual void goToClass() = 0; //上课
virtual void statusToClass() = 0; //状态
virtual void stopToClass() = 0; //下课
};
//抽象类,父类中为纯虚函数,子类继承了父类的纯虚函数,所以所有的纯虚函数必须重写override
class englishTeacher:public teacher{
public:
void goToClass() override;
void statusToClass() override;
void stopToClass() override;
};
void englishTeacher::goToClass(){
cout << "开车去上课" << endl;
}
void englishTeacher::statusToClass(){
cout << "爱学英语的老师" << endl;
}
void englishTeacher::stopToClass(){
cout << "溜之大吉" << endl;
}
int main()
{
//子类继承父类中的纯虚函数 所有纯虚函数必须重写
englishTeacher e1;
e1.goToClass();
e1.statusToClass();
e1.stopToClass();
//以多态的形式
teacher* t1 = new englishTeacher;
t1->goToClass();
t1->statusToClass();
t1->stopToClass();
//父类是抽象类 不能实例化,只能通过指针多态
//teacher t1;
return 0;
}