C++核心编程<类和对象>(4)
C++核心编程<类和对象>
- 4.类和对象
- 4.1封装
- 4.1.1封装的意义
- 封装的意义1
- 封装的意义2
- 4.1.2struct和class区别
- 4.1.3成员属性设置为私有
- 4.2对象的初始化和清理
- 4.2.1构造函数和析构函数
- 1.1构造函数语法:类名(){}
- 1.2析构函数语法: ~类名(){}
- 4.2.2构造函数的分类及调用
- 2.1构造函数的分类
- 2.2构造函数的调用
- 4.2.3拷贝构造函数调用时机
- 遗留问题(待解决)
- 4.2.4构造函数调用规则
- 4.2.5深拷贝与浅拷贝
- 4.2.6初始化列表
- 4.2.7类对象作为类成员
- 4.2.8静态成员
- 8.1静态成员变量(有访问权限)
- 8.2静态成员函数(都有访问权限)
- 4.3C++对象模型和this指针
- 4.3.1成员变量和成员函数分开存储
- 4.3.2this指针概念
- 4.3.3空指针访问成员函数
- 4.3.4const修饰成员函数
- 4.1常函数
- 4.2常对象
- 4.4友元
- 4.4.1友元的三种实现
- 1.1全局函数做友元
- 1.2类做友元
- 1.3成员函数做友元
- 4.5运算符重载
- 4.5.1加号运算符重载
- 4.5.2左移运算符重载
- 4.5.3递增运算符重载
- 4.5.4赋值运算符重载
- 4.5.5关系运算符重载
- 4.5.6函数调用运算符重载
- 4.6继承
- 4.6.1继承的基本语法
- 4.6.2继承方式
- 2.1继承方式共有三种
- 1.1公共继承
- 1.2保护继承
- 1.3私有继承
- 4.6.3继承中的对象模型
- 3.1打开开发人员命令提示工具查看对象模型
- 4.6.4继承中的构造和析构顺序
- 4.6.5继承同名成员处理方式
- 4.6.6继承同名静态成员处理方式
- 4.6.7多继承语法
- 4.6.8菱形继承
- 8.1菱形继承概念
- 4.7多态
- 4.7.1多态的基本概念
- 1.1多态分为两类
- 1.2静态多态和动态多态
- 1.3动态多态满足条件
- 1.4动态多态使用
- 4.7.2纯虚函数和抽象函数
- 2.1抽象类特点
- 4.7.3虚析构和纯虚析构
- 3.1虚析构和纯虚析构共性
- 3.2虚析构和纯虚析构区别
- 3.3语法
4.类和对象
- C++面向对象的三大特性: 封装、继承、多态
4.1封装
4.1.1封装的意义
封装的意义1
- 在设计类的时候,属性和行为写在一起,表现事物
- 语法
class 类名{访问权限: 属性 / 行为}
- 案例
#include<iostream>
using namespace std;const double PI = 3.14;
class Circle {// 访问权限
public:// 属性double m_r;// 行为double calculateZC() {return 2 * PI * m_r;}
};int main() {// 实例化// 通过圆类 创建具体的圆(对象)Circle cl;// 给圆对象的属性进行赋值cl.m_r = 5;cout << "圆的周长为:" << cl.calculateZC() << endl;system("pause");return 0;
}
封装的意义2
- 访问权限有三种
- public 公共权限
- 成员 类内可以访问 类外可以访问
- protected 保护权限
- 成员 类内可以访问 类外不可以访问 继承中子类可以访问父类
- private 私有权限
- 成员 类内可以访问 类外不可以访问 继承中子类不可以访问父类
- public 公共权限
4.1.2struct和class区别
- 在C++中struct和class唯一的区别就在于默认的访问权限不同
- 区别:
- struct 默认权限为公共
- class 默认权限为私有
#include<iostream>
using namespace std;
// struct 和 class区别
// struct 默认权限为公共
// class 默认权限为私有
class Circle {double PI = 3.14;double c_r = 0;void setR(int r = 5) {c_r = r;}void circleArea() {cout << "圆的面积为:" << PI * c_r * c_r << endl;}
};struct Person {int pAge = 15;string pName = "张三";void setName(string name) {pName = name;}void showInfo(){cout << "学生年龄:" << pAge << endl;cout << "学生姓名:" << pName << endl;}
};int main() {Circle cir;// 默认私有权限// cir.setR(10);Person per;per.setName("李四");per.showInfo(); per.pAge = 18;per.showInfo();system("pause");return 0;
}
4.1.3成员属性设置为私有
- 优点
- 将所有成员属性设置为私有,可以自己控制读写权限
- 对于写权限,可以检测数据的有效性
- 案例
#include<iostream>
#include<string>
using namespace std;
// 成员属性设置为私有
// 1.可以自己控制读写权限
// 2.对于写可以检测数据的有效性class Person {
public:// 编辑姓名void setName(string name) {m_Name = name;}//获取姓名string getName() {return m_Name;}//设置年龄 (设置数据的有效性,默认为0)void setAge(int age) {if (age < 0 || age >150) {m_Age = 0;cout << "设置年龄有误" << endl;return;}m_Age = age;}//获取年龄int getAge() {return m_Age;}//设置爱好void setHobby(string hobbies) {m_Hobbies = hobbies;}
private://姓名 rwstring m_Name;//年龄 rwint m_Age;//爱好 wstring m_Hobbies;
};int main() {//struct 和 class 区别// struct 默认权限是 公用 public// class 默认权限是 私有 privatePerson person;person.setName("yinbb是真的大佬");cout << "原神大佬:" << person.getName() << endl;person.setAge(120);cout << "年龄:" << person.getAge() << endl; // 120person.setHobby("篮球");// 不能获取爱好// cout << "爱好:" << person.getHobby() << endl;system("pause");return 0;
}
4.2对象的初始化和清理
4.2.1构造函数和析构函数
- 对象的初始化和清理也是两个非常重要的安全问题
- 一个对象或者变量没有初始状态,对其使用后果是未知的
- 同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题
- c++利用了构造函数和析构函数解决上述问题:
- 构造函数: 主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用
- 析构函数: 主要是作用在于对象销毁前系统自动调用,执行一些清理工作
1.1构造函数语法:类名(){}
- 构造函数,没有返回值也不写void
- 函数名称和类名相同
- 构造函数可以有参数,因此可以发生重载
- 程序在调用对象时候会自动调用构造,无需手动调用,而且只会调用一次
1.2析构函数语法: ~类名(){}
- 析构函数,没有返回值也不写void
- 函数名称和类名相同,在名称前加上符号~
- 析构函数不可以有参数,因此不可以发生重载
- 程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次
#include<iostream>
using namespace std;//对象的初始化和清理class Person {
public://构造函数Person() {cout << "构造函数调用" << endl;}//析构函数~Person() {cout << "析构函数调用" << endl;}
};// 在栈上的数据,test()执行完毕后,释放这个对象void test() {Person p;
}int main() {test();// 构造函数调用 析构函数调用//在main函数执行完毕,才会调用析构函数Person p;// 构造函数调用system("pause");return 0;
}
4.2.2构造函数的分类及调用
2.1构造函数的分类
- 两种分类方式
- 按参数分为: 有参构造和无参构造
- 按类型分为:普通构造和拷贝构造
2.2构造函数的调用
- 三种调用方式
- 括号法
- 显示法
- 隐式转换法
#include<iostream>
using namespace std;// 构造函数的分类及调用
class Person {
public:// 无参构造Person() {cout << "构造函数的调用(默认)" << endl;}// 有参构造Person(int num) {age = num;cout << "构造函数的调用(有参)" << endl;}// 拷贝构造函数Person(const Person &person) {age = person.age;cout << "构造函数的调用(拷贝)" << endl;}// 析构函数~Person() {cout << "析构函数调用" << endl;}
private:int age;
};// 调用
void test(){// 1.括号法// 调用无参构造Person p1;// 调用有参构造Person p2(21);// 调用拷贝构造函数Person p3(p2);// 注意事项// 调用默认构造函数时候,不要添加括号;编译器会认为是一个函数声明,不认为在创建对象// 2.显示法Person p11;Person p22 = Person(20);Person p33 = Person(p22);// Person(20);//匿名对象,特点: 当前行执行结束后,系统会立即回收匿名对象// 注意事项2// 不要利用拷贝构造函数 初始化匿名对象 编译器会认为Person (p3) === Person p3;对象声明// 3.隐式法Person p4 = 10;//相当于 写了 Person p4 = Person(10); 有参构造Person p5 = p4;}int main() {test();system("pause");return 0;
}
4.2.3拷贝构造函数调用时机
- C++中拷贝构造函数调用时机通常有三种情况
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
#include<iostream>
using namespace std;class Person {
public:Person() {cout << "Person默认构造函数的调用" << endl;}Person(int age) {m_Age = age;cout << "Person有参构造函数的调用" << endl;}Person(const Person& person) {m_Age = person.m_Age;cout << "Person拷贝构造函数的调用" << endl;}~Person() {cout << "Person析构函数的调用" << endl;}int m_Age;
};// 1.使用一个已经创建完毕的对象来初始化一个新对象
void test1() {Person p1(20);Person p2(p1);cout << "P2的年龄: " << p2.m_Age << endl;
}
// 2.值传递的方式给函数参数传值
Person doWork(Person p) {p.m_Age = 25;return p;
}
void test2() {Person p(12);doWork(p);cout << "p的年龄:" << p.m_Age << endl;//12cout << "拷贝构造调用之后对值的修改:" << (doWork(p)).m_Age << endl; //25
}// 3.值方式返回局部对象
Person doWork2() {Person p1;return p1;}
void test3() {Person p3 = doWork2();
}int main() {// test1();// test2();test3();system("pause");return 0;
}
遗留问题(待解决)
- 以值方式返回局部对象,拷贝函数未执行(不符合) 显示:由于G++优化导致,RVO有相关技术详情
4.2.4构造函数调用规则
-
默认情况下,c++编译器至少给一个类添加3个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
-
构造函数的调用规则如下
- 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
- 如果用户定义拷贝构造函数,c++不会提供其他普通构造函数
#include<iostream>
using namespace std;
// 构造函数的调用规则class Person {
public:Person() {cout << "Person的默认构造函数调用" << endl;}Person(int age) {cout << "Person的有参构造函数调用" << endl;m_Age = age;}Person(const Person &p) {m_Age = p.m_Age;cout << "person的拷贝构造函数调用" << endl;}~Person() {cout << "person的析构函数调用" << endl;}int m_Age;
};void test1() {Person p;p.m_Age = 18;/*如果不自定义拷贝函数,编译器也会提供默认的拷贝函数,进行值拷贝 p2.m_Age 依旧为 18*/Person p2(p);cout << "P2的年龄是:" << p2.m_Age << endl; //18
}void test2() {Person p(28);Person p2(p);cout << "p2的年龄:" << p2.m_Age << endl;// 28
}void test3() {Person p;
}int main() {// test1();// test2();// 如果只提供拷贝函数,其他普通函数不提供test3();// errorsystem("pause");return 0;
}
4.2.5深拷贝与浅拷贝
- 深浅拷贝的区别
- 浅拷贝:简单的赋值拷贝操作
- 深拷贝:在堆区重新申请空间,进行拷贝操作
#include<iostream>
using namespace std;
class Person {
public:Person() {cout << "默认构造函数的调用" << endl;}Person(int age,int height) {cout << "有参构造函数的调用" << endl;m_Age = age;m_Height = new int(height);}~Person() {// 析构代码,将堆区开辟数据做释放操作if (m_Height != NULL) {delete m_Height;// 防止野指针m_Height = NULL;}cout << "析构函数的调用" << endl;}//解决拷贝函数的问题(浅拷贝)Person(const Person &p) {cout << "Person拷贝构造函数调用" << endl;m_Age = p.m_Age;// m_Height = p.m_Height; 编译器默认实现就是这行代码// 深拷贝m_Height = new int(*p.m_Height);}int m_Age;int* m_Height;};/*
* 栈区先进后出
* p2释放执行后,p1再去释放(非法操作)
* 浅拷贝带来的问题就是堆区内存重复释放
*/
void test1() {Person p1(18,180);cout << "p1的年龄:" <<p1.m_Age<<"身高为:" <<*p1.m_Height<< endl; // 18 180Person p2(p1);cout << "p1的年龄:" << p2.m_Age << "身高为:" << *p2.m_Height << endl;// 18 180}int main() {test1();system("pause");return 0;
}
4.2.6初始化列表
- 作用
- C++提供了初始化列表语法,用来初始化属性
- 语法
- 构造函数(): 属性1(值1),属性2(值2),…{}
#include<iostream>
using namespace std;class Person {
public://传统方式/*Person(int a, int b, int c) {m_A = a;m_B = b;m_C = c;}*/// 初始化列表初始化属性Person(int a,int b,int c) : m_A(a), m_B(b), m_C(c) {}int m_A;int m_B;int m_C;
};void test1() {//Person p(1, 2, 30);Person p(10,50,100);cout << p.m_A << endl;cout << p.m_B << endl;cout << p.m_C << endl;
}
int main() {test1();system("pause");return 0;
}
4.2.7类对象作为类成员
- C++类中的成员可以是另一个类的对象,我们称为该成员为 对象成员
#include<iostream>
#include<string>
using namespace std;
class Phone {
public:Phone(string pName,float pPrice): p_Name(pName),p_Price(pPrice) {cout << "手机的构造函数调用" << endl;}~Phone() {cout << "手机的析构函数调用" << endl;}// 手机品牌string p_Name;// 手机价格float p_Price;
};
class Person {
public:Person(string name,Phone phone): m_Name(name),m_Phone(phone) {cout << "人的构造函数调用" << endl;}~Person() {cout << "人的析构函数调用" << endl;}//姓名string m_Name;//物品Phone m_Phone;
};// 当其他类对象作为本类成员,构造时候先构造类对象,再构造自身;析构的顺序相反
void test1() {Phone ph("华为", 7999);Person p("张三", ph);
}
int main() {test1();system("pause");return 0;
}
4.2.8静态成员
- 静态成员就是成员变量和成员函数前加上关键字static,称为静态成员
8.1静态成员变量(有访问权限)
- 所有对象共享同一份数据
- 在编译阶段分配内存(全局区)
- 类内声明,类外初始化
#include<iostream>
using namespace std;// 静态成员变量
class Person {
public:static int m_A;
};
int Person::m_A = 100;
void test1() {Person p;cout << p.m_A << endl; // 100Person p2;p2.m_A = 200;cout << p.m_A << endl;// 200}
void test2() {//静态成员变量 不属于某一个对象上,所有对象共享同一份数据//1.通过对象进行访问Person p;cout << p.m_A << endl;//2.通过类名进行访问cout << Person::m_A << endl;
}
int main() {//test1();test2();system("pause");return 0;
}
8.2静态成员函数(都有访问权限)
- 所有对象共享同一个函数
- 静态成员函数只能访问静态成员变量
#include<iostream>
using namespace std;class Person {
public:static void func() {m_A = 200;// 静态成员函数可以访问静态成员变量// m_B = 300; // 静态成员函数不能访问非静态成员变量 无法区分那个对象的属性cout << "static void func的调用" << endl;}static int m_A;int m_B;
};
int Person::m_A = 100;
void test1() {//1.通过对象访问Person p;p.func();//2.通过类名访问Person::func();
}int main() {test1();system("pause");return 0;
}
4.3C++对象模型和this指针
4.3.1成员变量和成员函数分开存储
- 只有非静态成员变量才属于类的对象上
#include<iostream>
using namespace std;
class Person {int m_A;// 非静态成员变量 属于类的对象上static int m_B;// 静态成员变量 不属于类对象上void func() {} // 非静态成员函数 不属于类对象上static void func2(){}// 静态成员函数 不属于类对象上
};
int Person::m_B = 100;
void test1() {Person p;// c++编译器会给每个对象分配一个字节空间,是为了区分空对象占内存的位置// 每个对象也应该有一个独一无二的内存地址cout << "size of p = " << sizeof(p) << endl;// 4
}
int main() {test1();system("pause");return 0;}
4.3.2this指针概念
- this指针指向被调用的成员函数所属的对象
- this指针的用途
- 当形参和成员变量同名时,可以用this指针来区分
- 在类的非静态成员函数中返回对象本身,可使用return *this
#include<iostream>
using namespace std;class Person {
public:Person(int age) {// this指针指向 被调用的成员函数 所属的对象this->age = age;}// 返回Person值对象,会默认调用拷贝构造函数,复制一份当前所调对象Person& PersonAddAge(Person &p) {this->age += p.age;return *this;}int age;
};// 解决名称冲突
void test1() {Person p(18);cout << "年龄大小:" << p.age << endl;// 18
}
// 返回对象本身用*this
void test2() {Person p1(10);Person p2(20);p2.PersonAddAge(p1).PersonAddAge(p1);cout << "年龄:" << p2.age << endl;// 40}int main() {//test1(); test2();system("pause");return 0;
}
4.3.3空指针访问成员函数
- C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针,如果用到this指针,需要加以判断保证代码的健壮性
#include<iostream>
using namespace std;
// 空指针调用成员函数
class Person
{
public:void showClassName() {cout << "this is Person class" << endl;}// m_Age 默认 this->m_Age// 报错原因 传入的指针为NULLvoid showPersonAge() {// 提高健壮性if (this == NULL) {return;}cout << "age=" << m_Age << endl;}int m_Age;
};void test01() {// 空指针Person* p = NULL;p->showClassName();p->showPersonAge();}int main() {test01();system("pause");return 0 ;
}
4.3.4const修饰成员函数
4.1常函数
- 成员函数后加const后,我们称为这个函数为常函数
- 常函数内不可以修改成员属性
- 成员属性声明是加关键词mutable后,在常函数总依然可以修改
4.2常对象
- 声明对象前加const称该对象为常对象
- 常对象只能调用常函数
#include<iostream>
using namespace std;
// 常函数
class Person
{
public:// this指针的本质 是指针常量 指针的指向是不可以修改的// const Person * const this;// 在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改void showPerson() const{this->m_B = 120;// this->m_A = 100;// this = NULL; //this指针不可以修改指针指向的}void showNum() {m_A = 130;}int m_A;mutable int m_B;// 特殊变量,即使在常函数中可以修改,加mutable关键词
};void test1() {Person p;p.showPerson();
}
void test2() {const Person p2;// 在对象前面加上const,变为常对象//p2.m_A = 100;// m_B是特殊值,在常对象下也可以修改p2.m_B = 120;p2.showPerson();// 常对象,不可以调用普通成员函数,因为普通成员函数可以修改属性// p2.showNum();
}int main() {test2();system("pause");return 0;
}
4.4友元
- 在程序中,有些私有属性 也想让类外的特殊的一些函数或类进行访问,就需要用到友元的技术
- 友元的关键词为friend
4.4.1友元的三种实现
1.1全局函数做友元
#include<iostream>
#include<string>
using namespace std;class Building
{//GoodF全局函数是Building的友元,可以访问Building中的私有成员friend void goodF(Building* building);
public:string m_SittingRom;Building() {m_SittingRoom = "客厅";m_BedRoom = "卧室";}
private:string m_BedRoom;
};
// 全局函数
void goodF(Building* building)
{cout << "全局函数被调用,当前访问:"<< building->m_SittingRoom << endl;// 添加了friend关键词,可以访问私有成员cout << "全局函数被调用,当前访问:" << building->m_BedRoom << endl;
}void test1() {Building building;goodF(&building);
}int main() {test1();system("pause");return 0;
}
1.2类做友元
#include<iostream>
#include<string>
using namespace std;
// 类做友元
class Building;
class GoodFri
{
public:GoodFri();void visit();Building* building;
};
class Building
{friend class GoodFri;
public:Building();
public:string m_SittingRoom;
private:string m_BedRoom;
};//类外写成员函数
Building::Building()
{m_SittingRoom = "客厅";m_BedRoom = "卧室";
}
GoodFri::GoodFri()
{// 创建建筑物对象building = new Building;
}
void GoodFri::visit()
{cout << "正在访问:" << building->m_SittingRoom << endl;cout << "正在访问:" << building->m_BedRoom << endl;}void test1() {GoodFri gf;gf.visit();
}int main() {test1();system("pause");return 0;
}
1.3成员函数做友元
#include<iostream>
#include<string>
using namespace std;
class Building;
class GoodFri;class GoodFri
{
public:GoodFri();void visit();// visit()函数能访问Building中的私有成员void visit2();// visit2()函数不能访问Building中的私有成员Building *building;
};
class Building
{friend void GoodFri::visit();
public:Building();
public:string m_SittingRoom;
private:string m_BedRoom;};Building::Building()
{m_SittingRoom = "客厅";m_BedRoom = "卧室";
}GoodFri::GoodFri()
{building = new Building;
}void GoodFri::visit()
{cout << "当前访问的位置:" << building->m_SittingRoom << endl; //客厅cout << "当前访问的位置:" << building->m_BedRoom << endl;// 卧室
}
void GoodFri::visit2()
{cout << "当前访问的位置:" << building->m_SittingRoom << endl;// 客厅//cout << "当前访问的位置:" << building->m_BedRoom << endl;// error
}void test1() {GoodFri gf;gf.visit();gf.visit2();
}
int main() {test1();system("pause");return 0;
}
4.5运算符重载
- 运算符重载概念:
- 对已有的运算符重新进行定义,赋予起另一种功能,以适应不同的数据类型
4.5.1加号运算符重载
#include<iostream>
using namespace std;//加号运算符重载
class Person
{
public://成员函数重载+号/** 成员函数重载的本质调用* Person p3 = p1.operator+(p2);*///Person operator+(Person &p);int m_A;int m_B;
};//Person Person::operator+(Person& p) {
// Person temp;
// temp.m_A = this->m_A + p.m_A;
// temp.m_B = this->m_B + p.m_B;
// return temp;
//}//全局函数重载+号
/*
* 全局函数重载本质调用
* Person p3 = operator+(p1,p2)
*/
Person operator+(Person& p1,Person &p2) {Person temp;temp.m_A = p1.m_A + p2.m_A;temp.m_B = p1.m_B + p2.m_B;return temp;
}void test1()
{Person p1;p1.m_A = 20;p1.m_B = 30;Person p2;p2.m_A = 40;p2.m_B = 20;Person p3 = p1 + p2;cout << "m_A=" << p3.m_A << endl;cout << "m_B=" << p3.m_B << endl;}
int main() {test1();system("pause");return 0;
}
4.5.2左移运算符重载
- 直接打印对象的属性
#include<iostream>
using namespace std;
class Person
{friend ostream& operator<<(ostream& cout, Person& p);
public:Person(int a,int b) {m_A = a;m_B = b;}
private://成员函数重载 左移运算符/** 不会利用成员函数重载<<运算符,因为无法实现 cout在左侧 * *///void operator<<() {};int m_A;int m_B;
};//全局函数重载左移运算符
ostream& operator<<(ostream &cout, Person &p)
{cout << "m_A = " << p.m_A << " m_B = " << p.m_B << endl;return cout;
}void test1() {Person p(10,12);cout << p << endl;
}int main() {test1();system("pause");return 0;
}
4.5.3递增运算符重载
- 通过重载递增运算符,实现自己的整型数据
#include<iostream>
using namespace std;
class MyInteger
{friend ostream& operator<<(ostream& cout, MyInteger myInt);
public:MyInteger() {m_NUM = 0;}//重载前置++运算符MyInteger& operator++();//重置后置++运算符 int代表占位参数,可以用于区分前置和后置递增MyInteger operator++(int);
private:int m_NUM;
};
MyInteger& MyInteger::operator++()
{m_NUM++;return *this;
}
MyInteger MyInteger::operator++(int) {// 先 记录当时结果 MyInteger temp = *this;// 后 递增m_NUM++;// 最后将记录结果做返回return temp;
}//重载<<运算符
ostream& operator<<(ostream& cout, MyInteger myInt)
{cout << myInt.m_NUM;return cout;
}void test1() {MyInteger myInteger;cout << ++myInteger << endl;cout << myInteger << endl;
}
void test2() {MyInteger myInteger;cout << myInteger++ << endl;cout << myInteger << endl;
}
int main() {//test1();test2();system("pause");return 0;
}
4.5.4赋值运算符重载
- c++编译器至少给一个类添加4个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝函数,属性进行值拷贝
- 赋值运算符operator=,对属性进行值拷贝
#include<iostream>
using namespace std;
class Person
{
public:Person(int age){m_Age = new int(age);}~Person(){if (m_Age != NULL) {delete m_Age;m_Age = NULL;}}Person& operator=(Person &p);int* m_Age;
};
Person& Person::operator=(Person& p)
{//先判断是否有属性在堆区,如果有先释放,在做深拷贝if (m_Age != NULL) {delete m_Age;m_Age = NULL;}//深拷贝m_Age = new int(*p.m_Age);return *this;
}
void test1()
{Person p1(18);cout << "年龄:" << *p1.m_Age << endl;Person p2(20);cout << "年龄: " << *p2.m_Age << endl;p2 = p1;cout << "年龄: " << *p2.m_Age << endl;
}int main()
{test1();system("pause");return 0;}
4.5.5关系运算符重载
- 重载关系运算符,可以自定义类型对象进行对比操作
#include<iostream>
using namespace std;
class Person;class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;}bool operator==(Person &p);string m_Name;int m_Age;
};bool Person::operator==(Person& p)
{if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) {return true;}return false;
}void test1() {Person p1("Tom", 20);Person p2("Tom", 23);if (p1 == p2) {cout << "p1和p2是相等的" << endl;}else {cout << "p1和p2是不相等的" << endl;}}int main() {test1();system("pause");return 0;
}
4.5.6函数调用运算符重载
- 函数调用运算符()也可以重载
- 由于重载后使用的方式非常像函数的使用,因此称为仿函数
- 仿函数没有固定写法,非常灵活
#include<iostream> #include<string> using namespace std;class MyPoint { public://重载函数调用运算符void operator()(string str); }; void MyPoint::operator()(string str) {cout << "重载函数调用运算符===>"<<str << endl; }void test1() {MyPoint myPoint;// 使用起来非常类似于函数调用,因此称为仿函数myPoint("hello world"); } int main() {test1();system("pause");return 0; }
4.6继承
4.6.1继承的基本语法
- 继承的好处
- 减少重复代码
- 语法
class 子类 : 继承方式 父类
#include<iostream>
using namespace std; Java页面
//class Java
//{
//public:
// void header();
// void footer();
// void left();
// void content();
//};
//
//void Java::header()
//{
// cout << "首页、公开课、登录、注册...(公共头部)" << endl;
//}
//void Java::footer()
//{
// cout << "帮助中心、交流中心、站内地图...(公告底部)" << endl;
//}
//void Java::left()
//{
// cout << "JAVA、Python、C++...(公共分类列表)" << endl;
//}
//void Java::content()
//{
// cout << "JAVA学科的视频" << endl;
//}
//
Python页面
//class Python
//{
//public:
// void header();
// void footer();
// void left();
// void content();
//};
//
//void Python::header()
//{
// cout << "首页、公开课、登录、注册...(公共头部)" << endl;
//}
//void Python::footer()
//{
// cout << "帮助中心、交流中心、站内地图...(公告底部)" << endl;
//}
//void Python::left()
//{
// cout << "JAVA、Python、C++...(公共分类列表)" << endl;
//}
//void Python::content()
//{
// cout << "Python学科的视频" << endl;
//}
//
CPP页面
//
Python页面
//class CPP
//{
//public:
// void header();
// void footer();
// void left();
// void content();
//};
//
//void CPP::header()
//{
// cout << "首页、公开课、登录、注册...(公共头部)" << endl;
//}
//void CPP::footer()
//{
// cout << "帮助中心、交流中心、站内地图...(公告底部)" << endl;
//}
//void CPP::left()
//{
// cout << "JAVA、Python、C++...(公共分类列表)" << endl;
//}
//void CPP::content()
//{
// cout << "C++学科的视频" << endl;
//}//公共部分
class BasePage
{
public:void header();void footer();void left();
};
void BasePage::header()
{cout << "帮助中心、交流中心、站内地图...(公告底部)" << endl;
}
void BasePage::left()
{cout << "JAVA、Python、C++...(公共分类列表)" << endl;
}
void BasePage::footer()
{cout << "帮助中心、交流中心、站内地图...(公告底部)" << endl;
}//JAVA页面
class Java : public BasePage
{
public:void content();
};
void Java::content()
{cout << "Java视频下载页面" << endl;
}//Python页面
class Python : public BasePage
{
public:void content();
};
void Python::content()
{cout << "Python视频下载页面" << endl;
}//C++页面
class CPP : public BasePage
{
public:void content();
};
void CPP::content()
{cout << "C++视频下载页面" << endl;
}void test1() {cout << "JAVA下载视频页面如下" << endl;Java ja;ja.header();ja.left();ja.content();ja.footer();cout << "---------------------------------" << endl;cout << "Python下载视频页面如下" << endl;Python py;py.header();py.left();py.content();py.footer();cout << "---------------------------------" << endl;cout << "C++下载视频页面如下" << endl;CPP cpp;cpp.header();cpp.left();cpp.content();cpp.footer();
}int main()
{test1();system("pause");return 0;
}
4.6.2继承方式
2.1继承方式共有三种

1.1公共继承
#include<iostream>
using namespace std;
//继承方式----公共继承
class Base1
{
public:int m_A;
protected:int m_B;
private:int m_C;
};class Son1 : public Base1
{
public:void func();
};
void Son1::func()
{//父类中的公共权限成员 到子类中依然是公共权限m_A = 10;//父类中的保护权限成员 到子类依然是保护权限m_B = 20;//父类中的私有权限成员 子类访问不了//m_C = 30;
}void test1()
{Son1 s1;s1.m_A = 12;//保护权限成员访问不到 类外访问不到// s1.m_B = 13;}
int main()
{test1();system("pause");return 0;
}
1.2保护继承
#include<iostream>
using namespace std;
//继承方式----公共继承
class Base1
{
public:int m_A;
protected:int m_B;
private:int m_C;
};class Son1 : protected Base1
{
public:void func();
};
void Son1::func()
{//父类中的公共权限成员 到子类中是保护权限m_A = 10;//父类中的保护权限成员 到子类依然是保护权限m_B = 20;//父类中的私有权限成员 子类访问不了//m_C = 30;
}void test1()
{Son1 s1;//保护权限成员访问不到 类外访问不到//s1.m_A = 12;//保护权限成员访问不到 类外访问不到// s1.m_B = 13;}
int main()
{test1();system("pause");return 0;
}
1.3私有继承
#include<iostream>
using namespace std;
//继承方式----公共继承
class Base1
{
public:int m_A;
protected:int m_B;
private:int m_C;
};class Son1 : private Base1
{
public:void func();
};
void Son1::func()
{//父类中的公共权限成员 到子类中是私有权限m_A = 10;//父类中的保护权限成员 到子类中是私有权限m_B = 20;//父类中的私有权限成员 子类访问不了//m_C = 30;
}void test1()
{Son1 s1;//私有权限成员访问不到 类外访问不到//s1.m_A = 12;//私有权限成员访问不到 类外访问不到// s1.m_B = 13;}
int main()
{test1();system("pause");return 0;
}
4.6.3继承中的对象模型
- 父类中所有非静态成员属性都会被子类继承下去
- 父类中私有成员属性 是被编译器给隐藏了 因此访问不到,但是确实被继承了
#include<iostream>
using namespace std;class Person
{
public:int m_A;
protected:int m_B;
private:int m_C;};
class Son :public Person
{
public:int m_D;
};void test1() {/** 父类中所有非静态成员属性都会被子类继承下去* 父类中私有成员属性 是编译器给隐藏了 因此访问不到,但是确实被继承了*/cout << "sizeof = " << sizeof(Son) << endl;// 16}int main()
{test1();system("pause");return 0;
}
3.1打开开发人员命令提示工具查看对象模型
- 找到文件所在位置(可用dir查看一下)
- 查看命令
cl /d1 reportSingleClassLayout类名 "文件名"
4.6.4继承中的构造和析构顺序
- 子类继承父类后,当创建子类对象,也会调用父类的构造函数
- 顺序
- 先构造父类,再构造子类
- 先子类析构,再父类析构
#include<iostream>
using namespace std;
class Person
{
public:Person() {cout << "调用父类的构造函数" << endl;}~Person() {cout << "调用父类的析构函数" << endl;}
};class Son : public Person
{
public:Son(){cout << "调用子类的构造函数" << endl;}~Son(){cout << "调用子类的析构函数" << endl;}
};void test1()
{/*调用父类的构造函数调用父类的析构函数*/// Person p;/*调用父类的构造函数调用子类的构造函数调用子类的析构函数调用父类的析构函数*/Son son;
}int main() {test1();system("pause");return 0;
}
4.6.5继承同名成员处理方式
- 访问子类同名成员 直接访问
- 访问父类同名成员 需要加作用域
- 子类中出现和父类同名的成员函数,子类的同名成员会隐藏父类中所有的同名成员函数
#include<iostream>
using namespace std;
class Base
{
public:Base(){m_A = 100;}void func(){cout << "Base of func的函数" << endl;}void func(int a){cout << "Base of func(int a)的函数" << endl;}int m_A;
};
class Son : public Base
{
public:Son() {m_A = 112;}void func(){cout << "Son of func的函数" << endl;}int m_A;
};
// 同名成员属性
void test1()
{Son son;cout << "Son of m_A = " << son.m_A << endl; // 112// 如果通过子类对象访问父类同名成员,需要加作用域cout << "Base of m_A = " << son.Base::m_A << endl; // 100}
// 同名成员函数
void test2()
{Son son;son.func(); //直接调用 调用是子类的func函数son.Base::func();//调用父类的func函数//如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏父类中所有的同名成员函数// son.func(120);son.Base::func(12);
}int main()
{// test1();test2();system("pause");return 0;
}
4.6.6继承同名静态成员处理方式
- 访问子类同名成员 直接访问即可
- 访问父类同名成员 需要加作用域
- 子类中出现和父类同名的静态成员函数,子类的同名成员会隐藏父类中所有的同名成员函数
#include<iostream>
using namespace std;
class Base
{
public:static void func(){cout << "Base of func()" << endl;}static int m_A;};
int Base::m_A = 100;class Son : public Base
{
public:static void func(){cout << "Son of func()" << endl;}static int m_A;
};
int Son::m_A = 120;// 同名成员属性
void test1()
{// 通过对象访问Son son;cout << "Son of m_A = " << son.m_A << endl;// 120cout << "Base of m_A = " << son.Base::m_A << endl; // 100// 通过类名访问cout << "Son of m_A = " << Son::m_A << endl; //120// 第一::代表通过类名方式访问 第二个::代表父类的作用域访问cout << "Base of m_A = " << Son::Base::m_A << endl;
}
// 同名成员函数
void test2()
{// 通过对象方式访问Son son;son.func();son.Base::func();// 通过类名方式访问/*Son of func()Base of func()*/Son::func();Son::Base::func();
}
int main()
{// test1();test2();system("pause");return 0;
}
4.6.7多继承语法
- C++允许一个类继承多个类
- 语法
class 子类: 继承方式 父类1,继承方式 父类2,...
- 实际开发环境不建议多继承
#include<iostream>
using namespace std;class Base1
{
public:Base1(){m_A = 110;}int m_A;
};class Base2
{
public:Base2(){m_A = 120;}int m_A;
};
class Base3
{
public:Base3(){m_A = 130;}int m_A;
};
class Son :public Base1, public Base2, public Base3
{
public:Son() {m_A = 200;}int m_A;
};void test1()
{Son son;cout << "Son of m_A = " << son.m_A << endl;// 200cout << "Base1 of m_A = " << son.Base1::m_A << endl;// 110cout << "Base3 of m_A = " << son.Base3::m_A << endl;// 130
}
int main()
{test1();system("pause");return 0;
}
4.6.8菱形继承
8.1菱形继承概念
- 两个派生类继承同一个基类
- 某一个类同时继承两个派生类
- 这种继承方式称为菱形继承或钻石继承
#include<iostream>
using namespace std;
/*
* 菱形继承的问题
* 菱形继承会导致继承两份数据,资源浪费
* 解决方式----虚继承 加上virtual
*/
// 动物类
class Animal
{
public:int m_Age;
};
// 羊类
class Sheep :virtual public Animal
{
public:};
//驼类
class Camel:virtual public Animal
{
public:};
class Alpaca:public Camel,public Sheep
{
public:};void test1()
{Alpaca alpaca;alpaca.Camel::m_Age = 5;alpaca.Sheep::m_Age = 8;//cout << "驼的年龄 age = " << alpaca.Camel::m_Age << endl;// 5//cout << "羊的年龄 age = " << alpaca.Sheep::m_Age << endl;// 8//添加了virtual关键词,实现虚继承cout << "驼的年龄 age = " << alpaca.Camel::m_Age << endl;// 8cout << "羊的年龄 age = " << alpaca.Sheep::m_Age << endl;// 8cout << alpaca.m_Age << endl;// 8
}
int main()
{test1();system("pause");return 0;
}

4.7多态
4.7.1多态的基本概念
1.1多态分为两类
- 静态多态
- 函数重载和运算符重载属于静态多态,复用函数名
- 动态多态
- 派生类和虚函数实现运行时多态
1.2静态多态和动态多态
- 静态多态的函数地址早绑定 - 编译阶段确定函数地址
- 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
1.3动态多态满足条件
- 有继承关系
- 子类重写父类的虚函数(virtual)
1.4动态多态使用
- 父类的指针或引用 指向子类的对象
#include<iostream>
using namespace std;
// 多态
//动物类
class Animal
{
public:virtual void speak();
};// 猫类
class Cat :public Animal
{
public:void speak();
};
void Animal::speak()
{cout << "动物在讲话" << endl;
}
void Cat::speak()
{cout << "喵~喵~喵~~~" << endl;
}
// 地址早绑定 在编译阶段确定函数地址
// 如果想执行猫在说话,那么这个函数地址就不能提前绑定,需要在运行阶段绑定,地址晚绑定
// 可以加virtual,实现晚绑定
void doSpeak(Animal& animal)
{animal.speak();
}void test1()
{Cat cat;// 未加virtual// doSpeak(cat);// 动物在说话doSpeak(cat);//喵~喵~喵~~
}
int main()
{test1();system("pause");return 0;
}

注意点: 上图默认在Cat类重写了speak()函数,如果不重写speak()函数,Cat类的虚拟函数指针指向的虚拟函数表依旧是&Animal::speak
4.7.2纯虚函数和抽象函数
2.1抽象类特点
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
- 当类有纯虚函数,这个类也称为抽象类
#include<iostream>
using namespace std;
// 纯虚函数 和 抽象类
class Base
{
public:virtual void func() = 0;
};
class Son : public Base
{
public:void func() {cout << "Son类重写Base的纯虚函数" << endl;}
};void test1() {//抽象类不能实例化// Base base;//new Base;// Son son;// 子类必须重写父类中的纯虚函数,否则无法实例化对象Base* base = new Son;base->func();
}
int main()
{test1();system("pause");return 0;
}
4.7.3虚析构和纯虚析构
- 多态使用时,如果子类中有属性开辟了堆区,那么父类指针在释放时,无法调用到子类的析构代码
- 解决方式
- 将父类中的析构函数改为虚析构或纯虚析构
3.1虚析构和纯虚析构共性
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
3.2虚析构和纯虚析构区别
- 如果是纯虚析构,该类属于抽象类,无法实例化对象
3.3语法
- 虚析构
virtual ~类名(){}
- 纯虚析构
virtual ~类名() = 0;
类名::~类名(){}
#include<iostream>
#include<string>
using namespace std;class Animal
{
public:Animal(){cout << "Animal构造函数调用" << endl;}// 利用虚析构可以解决,父类指针释放子类对象释放不干净的问题/*virtual ~Animal(){cout << "Animal析构函数调用" << endl;}*/// 纯虚析构virtual ~Animal() = 0;virtual void speak() = 0;
};
Animal::~Animal()
{cout << "Animal析构函数调用" << endl;
}class Cat:public Animal
{
public:Cat(string name) {cout << "Cat构造函数调用" << endl;this->m_Name = new string(name);}void speak(){cout <<*m_Name<< "喵~~喵~~~喵~~~~" << endl;}~Cat(){if (m_Name != NULL) {cout << "Cat的析构函数" << endl;delete m_Name;m_Name = NULL;}}string *m_Name;
};
void test1()
{Animal* am = new Cat("Tommy");am->speak();// 父类指针在析构时候,不会调用子类的析构函数,导致子类如果在堆区属性,出现内存泄露delete am;
}int main()
{test1();system("pause");return 0;
}
相关文章:
C++核心编程<类和对象>(4)
C核心编程<类和对象>4.类和对象4.1封装4.1.1封装的意义封装的意义1封装的意义24.1.2struct和class区别4.1.3成员属性设置为私有4.2对象的初始化和清理4.2.1构造函数和析构函数1.1构造函数语法:类名(){}1.2析构函数语法: ~类名(){}4.2.2构造函数的分…...
编写http workshop脚本从网页缓存里解析音乐
前一篇文章 编写http workshop脚本从网站下载音乐 示范了如何使用HttpClient访问API,以及Json数据的解析; 今天我们通过解析一个网页展示如何使用内置的LibXml2的功能解析HTML,提取我们关心的内容。 这里随便搜了2个资源类的网站,竟然使用的格式是一模一样的: https://www…...
当数字孪生遇上轨道交通,会有什么新发展?
轨道交通是城市间互通互联的命脉,是当下人们出行的首要选择之一,也是我国“新基建”的重点建设对象。将城轨交通各链路系统及多类型服务,与空间感知、移动互联、云计算等技术深度融合,集中实现城市空间、城轨分布、城轨运行动态的…...
原理底层计划--分布式事务
分布式事务 mysql事务 我们通过show engines查询存储引擎,mysql一般为innodb, 为什么? 因为innodb支持事务是原因之一。 特性无非ACID 原子性,一致性,隔离性,持久性 一致性是最后追求的结果,也就保证了数…...
Hive总结
文章目录一、Hive基本概念二、Hive数据类型三、DDL,DML,DQL1 DDL操作2 DML操作3 DQL操作四、分区操作和分桶操作1、分区操作2、分桶操作五、Hive函数六、文件格式和压缩格式一、Hive基本概念 Hive是什么? Hive:由 Facebook 开源用于解决海量结构化日志的…...
docker环境下安装jenkins
前言 差点被Jenkins的插件搞麻了,又是依赖不对又是版本需要升级的,差点破口大骂了,还好忍住了,静下心来慢慢搞,终于搞通了。这里必须记录一下。 废话不多说,上来就是干,jenkins是干嘛用的&…...
Shifu基础功能:设备接入
如何修改设备接入的配置 1. 编辑edgedevice.yaml文件 接入设备前,您需要对edgedevice.yaml文件进行编辑。对于不同的协议,protocolSettings可根据协议进行进一步配置,详细配置请前往Shifu API参考。 ... connection: Ethernet address: …...
基于Java+SpringBoot+Vue+Redis+RabbitMq的鲜花商城
基于JavaSpringBootVueRedisRabbitMq的鲜花商城 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取项目下载方式🍅 一、…...
蓝桥杯真题(解码)小白入!
本来看这个题感觉很简单,不就是Ascall值换来换去嘛,其实也真的这样,但是对于小白来说,ascall根本记不住 题目说了,每个数不会重复超过9次(这见到那多了,不然根本不会写) 其次如何实现…...
并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?
第20讲 | 并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别? 在上一讲中,我分析了 Java 并发包中的部分内容,今天我来介绍一下线程安全队列。Java 标准库提供了非常多的线程安全队列,很容易混淆。 今天我要问你的…...
分享四个前端Web3D动画库在Threejs中使用的动画库以及优缺点附地址
Threejs中可以使用以下几种动画库:Tween.js:Tween.js是一个简单的缓动库,可以用于在three.js中创建简单的动画效果。它可以控制数值、颜色、矢量等数据类型,并提供了多种缓动函数,例如线性、弹簧、强化、缓冲等等。区别…...
谷歌浏览器和火狐浏览器永久禁用缓存【一劳永逸的解决方式】
目录 前言 谷歌浏览器 方式一 方式二 火狐浏览器 前言 缓存对于开发人员来说异常的痛苦,很多莫名其妙的bug就是由缓存导致的,但当我们在网上查找禁用缓存的方式时,找到的方式大多数都是在开发者工具的面板中勾选禁用缓存的选项,但这种方式有个弊端就是需要一直打开这个…...
kibana查看日志
一、背景 kibana收集日志功能很强大,之前只是简单的使用,此次系统学习了解并分享一波 二、kibana查看日志的基本使用 1.选择查询的服务和日志文件 注意:每个应用配置了开发与生产环境,需要找到指定的应用 1.1选择对应的应用 1.…...
JS 异步接口调用介绍
JS 异步接口调用介绍 Js 单线程模型 JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。这样设计的方案主要源于其语言特性,因为 JavaScript 是浏览器脚本语言,它可以操纵 DOM ,可以渲染动画&a…...
5.深入理解HttpSecurity的设计
深入理解HttpSecurity的设计 一、HttpSecurity的应用 在前章节的介绍中我们讲解了基于配置文件的使用方式,也就是如下的使用。 也就是在配置文件中通过 security:http 等标签来定义了认证需要的相关信息,但是在SpringBoot项目中,我们慢慢脱离…...
opencv-python numpy常见的api接口汇总(持续更新)
前言 最近写代码总是提笔忘api,因为图像处理代码写的比较多,所以想着把一些常用的opencv的api,包括numpy的api做一个记录,后面再忘记的时候,就不用去google挨个搜索了,只需要在自己的博客中一查就全知道了…...
概率论小课堂:伯努利实验(正确理解随机性,理解现实概率和理想概率的偏差)
文章目录 引言I 伯努利试验1.1 伯努利分布(二项式分布)1.2 数学期望值(简称期望值)1.3 平方差(简称方差)1.4 标准差1.5 小结引言 假设买彩票中奖的概率是一百万分之一,如果要想确保成功一次,要买260万次彩票。你即使中一回大奖,花的钱要远比获得的多得多。 很多人喜…...
加密功能实现
文章目录1. 前言2. 密码加密1. 前言 本文 主要实现 对密码进行加密 ,因为 使用 md5 容易被穷举 (彩虹表) 而破解 ,使用 spring security 框架又太大了 (杀鸡用牛刀) 。 所以本文 就自己实现一个密码加密 . 2. 密码加密 这里我们通过 加盐是方式 来 对…...
大数据项目实战之数据仓库:用户行为采集平台——第1章 数据仓库概念
第1章 数据仓库概念 数据仓库(Data Warehouse),是为企业制定决策,提供数据支持的。可以帮助企业改进业务流程、提高产品质量等。 数据仓库的输入数据通常包括:业务数据、用户行为数据和爬虫数据等 业务数据…...
NTP对时服务器(NTP电子时钟)在生物制药业应用
NTP对时服务器(NTP电子时钟)在生物制药业应用 NTP对时服务器(NTP电子时钟)在生物制药业应用 8.1 系统概述 时钟系统为生物制药厂网络控制中心调度员、车场值班员及各部门工作人员提供统一的标准时间信息,也为本工程其它…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...
