C++语言基础:类和对象
- 9.4 类和对象
- C++面向对象的三大特征:封装、继承、多态
- C++认为万物皆为对象,对象上有属性和行为
- 具有相同性质的对象,可以抽象为类
- 人属于人类,车属于车类
- 9.4.1 封装
- 9.4.1.1 封装的意义
- (1)封装的意义
- 1. 将属性和行为作为一个整体,表现生活中的事物
- 2. 将属性和行为加以权限控制
- (2)封装意义一:
- 在设计类的之后,属性和行为写在一起,表现事物
- 语法:class 类名 { 访问权限: 属性 / 行为 }
- 类中的属性和行为统称为成员
- 1. 属性 -> 成员属性 -> 成员变量
- 2. 行为 -> 成员函数 -> 成员方法
- 类中的属性和行为统称为成员
- 示例1:设计一个圆类,求圆的周长
const double PI = 3.14; // 圆周率 class Circle { public: // 公共的访问权限int m_r; // 属性:半径double calculateZC() {return 2 * m_r * PI;} private: // 私有的访问权限 }; int main() {Circle c1; // 实例化: 通过圆类创建具体的圆(对象)c1.m_r = 10; // 圆对象的属性半径进行赋值cout << "圆的周长: " << c1.calculateZC() << endl; }
- 示例2:设计一个学生类,属性有姓名和学号
// 学生类 class Student { public:string m_name;int m_id;void showStudent() {cout << " 姓名: " << m_name << " 学号: " << m_id << endl;}void setName(string name) {m_name = name;}void setID(int id) {m_id = id;} }; int main() {Student s1;s1.m_name = "张三";s1.m_id = 1;s1.showStudent();s1.m_name = "李四";s1.m_id = 2;s1.showStudent(); }
- (3)封装意义二:
- 类在设计时,可以把属性和行为放在不同的权限下,加以控制
- 访问权限有三种
- 1. public 公共权限 类内可以访问 类外可以访问
- 2. protected 保护权限 类内可以访问 类外不可以访问 儿子可以访问父类中的保护内容
- 3. private 私有权限 类内可以访问 类外不可以访问 儿子不可以访问父类中的保护内容
- 示例:
class Person { public:string m_Name; // 姓名 protected:string m_Car; // 汽车 private:int m_psw; // 银行卡密码 public:void func() {m_Name = "张三";m_Car = "拖拉机";m_psw = 1234;} }; int main() {Person p1;p1.m_Name = "李四";// p1.m_Car = "奔驰"; // protected权限在类外不能访问// p1.m_psw = 123; // private权限在类外不能访问p1.func(); }
- (1)封装的意义
- 9.4.1.2 struct和class区别
- 在C++中 struct 和 class 唯一的区别就在于默认的访问权限不同
- 区别:
- struct 默认权限为公共
- class 默认权限为私有
- 示例
class C1 {int m_A; // 默认是私有权限 }; struct C2 {int m_A; // 默认是公共权限 }; int main() {C1 c1;// c1.m_A = 100; // 表述错误 默认权限是私有,类外无法访问C2 c2;c2.m_A = 100; // 默认权限是共有,类外可以访问 }
- 9.4.1.3 成员属性设置为私有
- 优点1:将所有成员属性设置为私有,可以控制读写权限
- 优点2:对于写权限,我们可以检测数据的有效性
- 示例:
class Person { public:void setName(string name) {m_Name = name;}string getName() {return m_Name;}void setAge(int age) {if (age < 0 || age > 150) { // 优点2:检测数据的有效性cout << " 年龄不合理 " << endl;return;}m_Age = age;}int getAge() {return m_Age;}void setLover(string lover) {m_Lover = lover;} private: // 优点1: 控制读写权限string m_Name; // 姓名 可读可写int m_Age; // 年龄 可读可写string m_Lover; // 朋友 只写 }; int main() {Person p;p.setName("张三");cout << "名字是:" << p.getName() << endl; }
- 9.4.1.1 封装的意义
- 9.4.2 对象的初始化和清理
- 9.4.2.1 构造函数和析构函数
- 1. 对象的初始化和清理是两个非常重要的安全问题
- 1. 一个对象或者变量没有初始化状态,对其是用后果是未知的
- 2. 统一的使用完一个对象或者变量,没有及时清理,也会造成一定的安全问题
- 2. 如果不提供构造函数和析构函数的实现,则编译器会提供构造函数和析构函数的空实现
- 1. 构造函数
- 作用:主要用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用
- 语法:类名( ) { }
- 1. 构造函数,没有返回值也不写void
- 2. 函数名与类名相同
- 3. 构造函数可以有参数,也可以重载
- 4. 程序在调用对象时会自动调用构造,无须手动调用,而且只会调用一次
- 2. 析构函数
- 作用:主要作用在于对象销毁前系统自动调用,执行一些清理工作
- 语法:~ 类名( ) { }
- 1. 析造函数,没有返回值也不写void
- 2. 析造函数名与类名相同,在名称前加~
- 3. 析造函数没有参数,不可以重载
- 4. 程序在销毁对象前会自动调用析造,无须手动调用,而且只会调用一次
- 示例:构造函数和析构函数
class Person { public:Person(){cout << "构造函数" << endl;}~Person(){cout << "析构函数" << endl;} }; int main() {Person p;system("pause");return 0; }
- 1. 构造函数
- 1. 对象的初始化和清理是两个非常重要的安全问题
- 9.4.2.2 构造函数的分类和使用
- 两种分类方式:
- 1. 按参数分为:有参构造和无参构造
- 2. 按类型分为:普通构造和拷贝构造
- 三种调用方式:
- 1. 括号法
- 2. 显示法
- 3. 隐式转换法
- 示例
// 构造函数的分类 class Person { public:Person() // 无参构造{cout << "构造函数-无参构造" << endl;}Person(int a) // 有参构造{age = a;cout << "构造函数-有参构造" << endl;}Person(const Person &p) // 拷贝构造函数{age = p.age; // 将传入人身上所有的属性拷贝到我身上cout << "拷贝构造函数" << endl;}~Person(){cout << "析构函数" << endl;}int age; };// 调用:三种调用方式 void test01() {// 1. 括号法//Person p; // 默认构造函数//Person p1(); // 无参构造函数//Person p2(10); // 有参构造函数//Person p3(p2); // 拷贝构造函数//cout << "p2的年龄为:" << p2.age << endl;//cout << "p3的年龄为:" << p3.age << endl;// 注意事项1// 调用默认构造函数的时候,不要加()// 2. 显示法Person p; // 默认构造函数Person p2 = Person(10); // 有参构造函数Person p3 = Person(p2); // 拷贝构造函数Person(10); // 匿名对象,特点:当前执行结束狗,系统会马上回收内存// 注意事项2// 不要利用拷贝构造函数,初始化匿名对象,编译器会认为这是一个对象的声明// Person(p3);// 3. 隐式转换法Person p4 = 10; // 相当于写了Person p4 = Person(10) // 有参构造函数Person p5 = p4; // 拷贝构造函数 } int main() {test01();system("pause");return 0; }
- 两种分类方式:
- 9.4.2.3 拷贝构造函数调用时机
- C++中拷贝构造函数调用时机通常有三种情况
- 1. 使用一个已经创建完毕的对象来初始化一个新对象
- 2. 值传递的方式给函数参数传值
- 3. 以值方式返回局部对象
- 示例
// 调用:三种调用方式, C++中拷贝构造函数调用时机通常有三种情况 //1. 使用一个已经创建完毕的对象来初始化一个新对象 void test01() {Person p1(20);Person p2(p1);cout << "p2的年龄:" << p2.age << endl; // 20 } //2. 值传递的方式给函数参数传值 void doWork(Person p) { }void test02() {Person p;doWork(p); } //3. 以值方式返回局部对象 Person doWork2() {Person p1;cout << "doWork2 p1的地址:" << (int*)&p1 << endl; // 006AFAB8return p1; } void test03() {Person p = doWork2();cout << "test03 p1的地址:" << (int*)&p << endl; // 006AFBB0 } int main() {test01();test02();test03();system("pause");return 0; }
- C++中拷贝构造函数调用时机通常有三种情况
- 9.4.2.4 构造函数调用规则
- 创建一个类,C++编译器会给每个类添加只是三个函数
- 1. 默认构造(空实现)
- 2. 析构函数(空实现)
- 3. 拷贝构造(值拷贝)
- 默认情况下,C++编译器至少给一个类添加3个函数
- 1. 默认构造函数(无参,函数体为空)
- 2. 默认析构函数(无参,函数体为空)
- 3. 默认拷贝构造函数,对属性进行值拷贝
- 构造函数调用规则如下:
- 1. 如果用户定义有参构造函数,C++不再提供无参构造,但是会提供默认拷贝构造
- 2. 如果用户定义拷贝构造函数,C++不会再提供其他构造函数
- 9.4.2.5 深拷贝和浅拷贝
- 深拷贝:在堆区重新申请空间,进行拷操作
- 浅拷贝:简单的赋值拷贝操作(浅拷贝的问题是堆区的内容重复释放)
- 总结:如果属性是在堆区开辟的,一定要在代码中提供深拷贝函数,防止浅拷贝带来的问题
- 示例
int* m_Height; Person(const Person &p) // 拷贝构造函数 {age = p.age; // 将传入人身上所有的属性拷贝到我身上// m_Height = p.m_Height; // 浅拷贝的实现方式,也是编译器默认的实现方式m_Height = new int(*p.m_Height); // 深拷贝的实现方式,开辟一块新法内存空间cout << "拷贝构造函数" << endl; }
- 9.4.2.6 初始化列表
- 作用:C++提供了初始化列表语法,用来初始化属性
- 语法:构造函数():属性1(值1),属性2(值2)... { }
- 示例
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 test01() {Person p(100,200,300);cout << p.m_A << p.m_B << p.m_C << endl; }
- 9.4.2.7 类对象作为类成员
- C++类中的成员可以是另一个类的对象,我们称该成员为对象成员
- 例如:B类中有对象A作为成员,A为对象成员
- class A {}
- class B{
- A a;
- }
- 例如:B类中有对象A作为成员,A为对象成员
- 那么当创建B对象的时候,A与B的构造和析构谁先谁后?
- 结论:A的构造 -> B的构造 -> B的析构 -> A的析构
- 构造的时候,先构造类内的对象,在构造自身,析构的时候相反。
- C++类中的成员可以是另一个类的对象,我们称该成员为对象成员
- 9.4.2.8 静态成员
- 1 静态成员就是在成员变量和成员函数前加上关键字static,称其为静态成员
- 2 静态成员分为:
- 1. 静态成员变量
- 1) 所有对象共享同一份数据
- 2) 在编译阶段分配内存
- 3) 类内声明,类外初始化
- 2. 静态成员函数
- 1) 所有对象共享同一个函数
- 2) 静态成员函数只能访问静态成员变量
- 1. 静态成员变量
- 3 访问静态成员的方式
- 1. 通过对象访问
- 2. 通过类名访问
- 示例
class Person { public:static void func() { // 静态成员函数m_A = 100; // 静态成员函数可以访问静态成员变量// m_B = 200; // 会报错,因为静态成员函数不可以访问非静态成员变量// 因为非静态成员变量,要通过对象进行访问,直接访问的话无法区分是哪个对象的成员变量cout << "static void func() 调用" << endl;}static int m_A; // 静态成员变量int m_B; // 非静态成员变量 private:// 静态成员函数也是有访问权限的static void func2() {cout << "static void func2() 调用" << endl;} }; int Person::m_A = 0; // 静态成员变量类外初始化 void test01() {Person p;p.func(); // 1. 通过对象访问Person::func(); // 2. 通过类名访问// Person::func2(); // 报错,类外无权访问 }
- 9.4.3 C++对象模型和this指针
- 9.4.3.1 成员变量和成员函数分开存储
- 在C++中,类的成员变量和成员函数分开存储
- 只有非静态成员变量才属于类的对象上
- tip
- 1. 空对象占用内存空间为 1
- 1 C++编译器为每个空对象分配一字节空间,是为了区分空对象栈内存的位置
- 2 每个空对象也有独一无二的内存地址
- 2.
- 1. 空对象占用内存空间为 1
- 示例
class Person { public:static int m_A; // 静态成员变量,不属于类对象上int m_B; // 非静态成员变量,属于类对象上int m_C; // 非静态成员变量,属于类对象上void func() {} // 非静态成员函数,不属于类对象上}; void test01() {Person p;cout << "size of P = " << sizeof(p) << endl; // 输出8,8字节是m_B和m_C占用的内存 }
- 9.4.3.2 this指针
- 每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
- 问题是:这一块代码是如何区分哪个对象调用自己的呢?
- C++通过提供特殊的对象指针,this指针,解决上述问题
- this指针指向被调用的成员函数所属的对象
- this指针是隐含每一个非静态成员函数内的一种指针
- this指针不需要定义,直接使用即可
- this指针的用途
- 1. 当形参和成员变量同名是,可用this指针来区分
- 2. 在类的非静态成员函数中返回对象本身,可使用return *this;
- 示例
class Person { public:Person(int age){this->age = age;}// 返回值必须是引用方式,如果不是引用方式返回,则无法返回对象本身,而是创建了一个新的对象返回Person& PersonAddAge(Person& p){this->age += p.age;return *this;}int age; }; void test01() {Person p1(10);Person p2(10);p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1); // 链式编程cout << "p2 age = " << p2.age << endl; // 输出40 }
- 9.4.3.3 空指针访问成员函数
- C++空指针也可以调用成员函数的,但是也要注意有没有用到this指针
- 如果用到this 指针,需要加以判断保证代码的健壮性
- 示例
class Person { public:void showClassName() {cout << "This is a Person class!" << endl;}void showPerson() {if (this == NULL) {return;}cout << "age = " << m_Age << endl;}int m_Age; }; void test01() {Person* p1 = NULL; // 这是一个空指针p1->showClassName();p1->showPerson(); }
- C++空指针也可以调用成员函数的,但是也要注意有没有用到this指针
- 9.4.3.4 const修饰成员函数
- 1.常函数
- 1. 成员函数加const后我们称这个函数为常函数
- 2. 常函数内不可以修改成员属性
- 3. 长远属性声明是加关键字mutable后,在常函数中依然可以修改
- 2. 常对象
- 1. 声明对象前加const称该对象为常对象
- 2. 常对象只能调用常函数
- 示例
class Person { public:// this 指针的本质 是指针常量 指针的指向是不可以修改的// 在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改// 下一行相当于 const Person * const this;void showPerson() const // 常函数{// this 指针可以表达为 Person* const this;// this = NULL; // 表达式错误,this指针不可以修改指针的指向// this->m_Age = 100; // 表达式错误,常函数内不可以修改成员属性this->m_B = 100;}void func() {}int m_Age;mutable int m_B; // 特殊变量,即使在常函数中也可以修改 };void test01() {Person p1;p1.showPerson(); }void test02() {const Person p2; //对象前加const,变为常对象// p2.m_Age = 100; // 表达式错误,常对象内不可以修改成员p2.m_B = 200; // m_B是特殊值,在常对象下也可以修改// p2.func(); // 表达式错误,常对象只能调用常函数 }
- 1.常函数
- 9.4.3.1 成员变量和成员函数分开存储
- 9.4.4 友元
- 9.4.4.1 友元介绍
- 比喻:客厅(public)、卧室(Private);
- 客厅所有客人都可以去,卧室私有仅仅自己能进
- 卧室也允许自己的朋友(friend)进去
- 程序中,有一些私有属性也可以让类外的特殊函数或者类访问,需要用到友元
- 友元的目的:让一个函数或者类访问另一个类的私有成员
- 友元关键字:friend
- 友元的三种实现
- 1. 全局函数做友元
- 2. 类做友元
- 3. 成员函数做友元
- 比喻:客厅(public)、卧室(Private);
- 9.4.4.2 全局函数做友元
- 9.4.4.3 类做友元
- 9.4.4.4 成员函数做友元
- 示例: 三种方式做友元
class Buliding {friend void goodGay(Buliding* buliding);// 方式1:全局函数做友元:goodGay全局函数是Buliding好朋友,可以访问Buliding中的私有成员friend class GoodGay; // 方式2:类做友元friend void BadGay::visit(); // 方式3:成员函数做友元public:Buliding() {m_SittingRoom = "客厅";m_BedRoom = "卧室";}public:string m_SittingRoom; // 客厅private:string m_BedRoom; // 卧室 };// 方式1:全局函数做友元 void goodGay(Buliding* buliding) {cout << "全局函数做友元 好朋友正在访问:" << buliding->m_SittingRoom << endl;cout << "全局函数做友元 好朋友正在访问:" << buliding->m_BedRoom << endl;// 方式1:全局函数做友元.访问私有成员 }// 方式2:类做友元 class GoodGay { public:GoodGay() {buliding = new Buliding;}void visit() { // 参观函数,访问building中的属性cout << "类做友元 好朋友正在访问:" << buliding->m_SittingRoom << endl;cout << "类做友元 好朋友正在访问:" << buliding->m_BedRoom << endl; // 方式2:类做友元.访问私有成员}Buliding* buliding; };// 方式3:成员函数做友元 class BadGay { public:BadGay();void visit();// visit1函数不可以访问BUliding中的私有成员void visit2(); //3.成员函数做友元: visit2参观函数,访问building中的私有成员Buliding* buliding; };BadGay::BadGay() {buliding = new Buliding; }void BadGay::visit() { //3.成员函数做友元: visit2参观函数,访问building中的私有成员cout << "普通函数 正在访问:" << buliding->m_SittingRoom << endl;cout << "成员函数做友元 好朋友正在访问:" << buliding->m_BedRoom << endl; // 方式2:类做友元.访问私有成员 }void BadGay::visit2() {cout << "成员函数做友元 好朋友正在访问:" << buliding->m_SittingRoom << endl; }void test01() {Buliding buliding;goodGay(&buliding); // 方式1:全局函数做友元GoodGay goodGay;goodGay.visit(); // 方式2:类做友元BadGay badGay;badGay.visit(); // 方式3:成员函数做友元 }
- 9.4.4.1 友元介绍
- 9.4.5 运算符重载
- 9.4.6 继承
- 9.4.6.1 继承的基本语法
- 语法:class 子类 : 继承方式 父类
- 示例:class A : public B;
- A类称为 子类 或者 派生类
- 子类中的成员,包含两大部分:
- 1. 一类是从基类继承过来的
- 2. 另一类是自己增加的成员
- 从基类继承过来的成员表现出其共性,新增的成员表现出其个性
- 子类中的成员,包含两大部分:
- B类称为 父类 或者 基类
- 语法:class 子类 : 继承方式 父类
- 9.4.6.2 继承方式
- 继承方式有三种:
- 1. 公共继承
- 2. 保护继承
- 3. 私有继承
- 三种继承方式展示
- 继承方式有三种:
- 9.4.6.3 继承中的对象模型
- 问题:从父类继承过来的成员,哪些属于子类对象中?
- 答案:父类中所有的非静态成员属性都会被子类继承。父类中私有成员属性是被编译器隐藏了,因此访问不到,但是依旧在子类中占有内存空间
- sizeof(子类) = sizeof(父类) + sizeof(子类新增的成员属性)
- 利用开发人员命令提示工具查看对象模型
- 1. 跳转到code所在的路径
- 2. 命令行输入具体的查看命令:
- cl /d1 reportSingleClassLayout类名 文件名
- 示例:
- cl /d1 reportSingleClassLayoutSon "01C++HelloWorld.cpp"
- cl /d1 reportSingleClassLayoutSon "01C++HelloWorld.cpp"
- 问题:从父类继承过来的成员,哪些属于子类对象中?
- 9.4.6.4 继承中构造和析构的顺序
- 子类继承父类后,当创建子类对象,也会调用父类的构造函数
- 问题:父类和子类的构造和析构顺序是谁先谁后?
- 答案:父构造 -> 子构造 -> 子析构 -> 父析构
- 9.4.6.5 继承同名成员处理方式
- 问题:当子类和父类出现同名的成员,如果通过子类对象,访问到子类或者父类中同名的数据呢?
- 访问子类同名成员:直接访问即可
- 访问父类同名成员:需要加作用域
- 如果子类中出现和父类同名的成员函数,子类的同名成员函数会将父类的同名成员函数全部隐藏
- 如果想要访问父类的同名成员函数,就需要加作用域
- 示例
- 问题:当子类和父类出现同名的成员,如果通过子类对象,访问到子类或者父类中同名的数据呢?
- 9.4.6.6 继承同名静态成员处理方式
- 问题:继承中同名静态成员在子类对象上如何访问?
- 静态和非静态出现同名时,处理方式一致
- 访问子类同名成员:直接访问即可
- 访问父类同名成员:需要加作用域
- 静态成员有特殊之处
- 静态成员可以通过类名访问,非静态成员不可以
- 示例:
- 静态和非静态出现同名时,处理方式一致
- 问题:继承中同名静态成员在子类对象上如何访问?
- 9.4.6.7 多继承语法
- C++允许一个类继承多个类
- 多继承可能会引发父类有同名成员出现,需要加作用域区分
- C++实际开发中不建议使用多继承
- 语法:class 子类:继承方式 父类1,继承方式 父类2…
- C++允许一个类继承多个类
- 9.4.6.8 菱形继承
- 概念:
- 1. 两个派生类继承同一个基类
- 2. 又有某个类同时继承这两个派生类
- 3. 这种继承称为菱形继承,或者钻石继承
- 问题:可能会出现二义性
- 解决:虚继承 virtual
- vdptr: 虚基类指针
- 示例
- 概念:
- 9.4.6.1 继承的基本语法
- 9.4.7 多态
- 9.4.7.1 多态的基本概念
- 多态分为两类:
- 1. 静态多态:函数重载 和 运算符重载 属于静态多态,复用函数名
- 2. 动态多态:派生类和虚函数实现运行时多态
- 静态多态和动态多态区别:
- 1. 静态多态的函数地址早绑定 - 编译阶段确定函数地址
- 2. 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
- 动态多态的条件
- 1、有继承关系
- 2、子类重写父类的虚函数
- 重写:子类中的函数返回值类型、函数名称、参数列表与父类完全相同
- 动态多态的使用
- 1、父类指针 指向 子类的对象
- 示例:
class Animal { public:// 虚函数virtual void speak() {cout << "动物在说话" << endl;} };class Cat : public Animal { public:void speak() {cout << "小猫在说话" << endl;} };// 执行说话的函数 // 如果不加virtual,地址早绑定,在编译阶段就确定了函数地址 // 如果想让子类的方法被调用,父类同名方法要加virtual void doSpeak(Animal &animal) {animal.speak(); }void test01() {Cat cat;doSpeak(cat); // 小猫在说话 }
- 多态的底层原理
- 工具查看底层时间的sizeof的大小
- 多态分为两类:
- 9.4.7.2 多态案例一:计算器
- 多态的优点:
- 1. 代码组织结构清晰
- 2.可读性强
- 3. 利于前期和后勤的扩展和维护
- 满足开发原则:对扩展开放、对修改关闭
- 多态的优点:
- 9.4.7.3 纯虚函数和抽象类
- 在多态中,通常父类的虚函数实现是毫无意义的,主要都是调用子类重写的内容
- 因此可以将虚函数改为纯虚函数
- 纯虚函数语法:virtual 返回值类型 函数名 (参数列表) = 0
- 当一个类中有了纯虚函数,这个类就被称为抽象类
- 抽象类的特点:
- 1. 无法实例化对象
- 2. 子类必须重写抽象类中的纯虚函数,否则也属性抽象类
- 抽象类的特点:
- 9.4.7.4 多态案例二:制作饮品
- 9.4.7.5 虚析构和纯虚析构
- 多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构代码
- 解决方式:将父类的析构函数改成虚析构或者纯虚析构
- 虚析构和纯虚析构的共性:
- 1、可以解决父类指针释放子类对象
- 2、都需要具体的函数实现
- 虚析构和纯虚析构的区别:
- 1、如果是纯虚析构,该类属于抽象类,无法实例化对象
- 虚析构语法:virtual ~类名( ){ }
- 纯虚析构语法:
- 1、virtual ~类名( ) = 0;
- 2、类名::~类名( ){ }
- 总结
- 1、虚析构和纯虚析构是用来解决父类指针释放子类对象的问题
- 2、如果子类没有堆区数据,可以不写为虚析构和纯虚析构
- 3、拥有纯虚析构的类也属于抽象类
- 多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构代码
- 9.4.7.6 多态案例三:电脑组装
- 9.4.7.1 多态的基本概念
- 9.4.2.1 构造函数和析构函数
- C++面向对象的三大特征:封装、继承、多态
C++语言基础:类和对象相关推荐
- Python基础(类与对象)
Python基础(类与对象) 一.编程的两大思想 面向过程和面向对象 面向过程 面向对象 区别 事物比较简单,可以用线性的思维去解决 事物比较复杂,使用简单的线性思维无法解决 共同点:面向对象和面向过 ...
- 【清华大学-郑莉教授】C++语言程序设计 类与对象
[清华大学-郑莉教授]C++语言程序设计 类与对象 面向对象程序设计的基本特点 类与对象的基础概念和语法 类和对象 构造函数和析构函数 默认构造函数 委托构造函数 复制构造函数 右值引用 移动构造函数 ...
- ios开发之OC基础-类和对象
ios开发之OC基础-类和对象 本系列的文章主要来自于个人在学习前锋教育-欧阳坚老师的iOS开发教程之OC语言教学视频所做的笔记,边看视频,边记录课程知识点.建议大家先过一遍视频,在看视频的过程中记录 ...
- JAVA设计一个fan类_北京信息科技大学java语言程序设计-类和对象
北京信息科技大学java语言程序设计-类和对象 [实验名称]实验2 类和对象 [实验目的] 1.学习和掌握Java创建对象的方法. 2.学习和掌握通过创建对象.使用类的方法. 3.学习和掌握Java类 ...
- Python基础——类与对象
Python基础--类与对象 Python基础--类与对象 类与对象 数据类型 对象 类的创建 创建语法 类的创建 对象的创建 对象调用类中的内容 类属性,类方法,静态方法 类属性 访问类属性 类方法 ...
- Java语言基础(常见对象3(数组排序(冒泡排序、选择排序、直接插入排序、快速排序、归并排序)、Arrays、基本数据类型包装类、Integer类、自动拆箱、自动装箱))
Java语言基础 常见对象 数组排序 冒泡排序 相邻元素两两比较,大的往后放,第一次完毕,最大值的元素就会出现在最大索引处:以此循环 //冒泡排序:public static void main(St ...
- ios c语言调用oc方法,ios开发之OC基础-类和对象(示例代码)
本系列的文章主要来自于个人在学习前锋教育-欧阳坚老师的iOS开发教程之OC语言教学视频所做的笔记,边看视频,边记录课程知识点.建议大家先过一遍视频,在看视频的过程中记录知识点关键字,把把握重点,然后再 ...
- python编程基础—类与对象
1.类的声明与定义 Python 是一门面向对象的语言. 面向对象编程 - Object Oriented Programming(简称 OOP)是一种编程思想,在面向对象编程中,把对象作为程序的基本 ...
- Scala面向对象基础--类和对象
一.类和对象介绍 在Scala里,类是用关键字"class"开头的代码定义,它是用于创建对象的蓝图.一个类就是一个类型,不同的类就是不同的类型,一个对象的类型就是创建它用的那个类. ...
- Lua 面向对象基础-类和对象(一)
目录 1. lua中创建对象 2.lua中定义类的思路 3.元表的概念 4.类的定义和实例化 5. 重写类的方法 lua是一门面向过程的脚本语言,lua语言对于事物.结构通通描述为 table,而类和 ...
最新文章
- @Transactional注解在什么情况下失效?
- 高可靠芯片搭配视觉演算法,影像式ADAS满足车规要求
- HDU 5727 Necklace
- C# 调用其他的动态库开发应注意的问题
- 简练软考知识点整理-公邀竞单询其
- 剑指offer面试题39. 数组中出现次数超过一半的数字(数组)(摩尔投票法)
- Vue之.sync 修饰符详解
- 15c语言语句_如何学好C语言判断语句?攻略if语句是第一步
- Failed to connect to Etherscan API at url https://api-rinkeby.etherscan.io/api
- Android API之android.view.View.MeasureSpec
- 未来教育c语言二级51套答案,未来教育版计算机二级C语言上机题库(含答案)
- java azure blobs sas_仅使用SAS令牌连接到Azure存储帐户?
- C++中的数学函数汇总
- adc网络语什么意思_王者荣耀游戏术语解释 ADC是什么意思
- 2020最新版python基础入门学习视频教程
- Cloudflare找不到服务器 IP 地址
- java发送带图片的邮件代码
- [10] Linux系统日常运维
- 学习笔记——Dialog回调
- python是什么?python又可以用来干什么?(详细讲解来啦)
热门文章
- Tuxera NTFS for Mac(mac读写NTFS磁盘工具)简体中文版
- 使用URL SCHEME启动淘宝客户端并跳转到某个商品页面的方法
- python是第几代编程语言-Python语言目前是世界上第几流行的计算机编程语言?
- Android最新面试实战总结
- iOS马甲包混淆方案怎么做?
- 说说regsvr32命令
- VMware16 安装CentOS8详细教程
- Tomcat启动报错“java.lang.ClassNotFoundException: org.apache.catalina.loader.DevLoader”
- 李宏毅DLHLP.20.Non-Autoregressive Generation
- 8位MCU要被淘汰了吗?