C++学习之旅 -类和对象(重点)

文章目录

      • 封装
        • 封装的意义
        • 案例1
        • 案例2
      • 访问权限
      • C++中class和struct的区别
      • 成员属性私有化
      • 构造函数和析构函数
        • 构造函数
        • 析构函数
        • 构造函数的分类以及调用
          • 构造&调用
      • 拷贝构造函数调用时机
      • 深拷贝&浅拷贝
      • 初始化列表
      • 类对象作为类成员
      • 静态成员
      • C++对象模型&this指针
        • 成员变量和成员函数分开存储
        • this指针
      • 空指针访问成员函数
      • const修饰成员函数
        • 常函数
        • 常对象
      • 友元
        • 1. 全局函数做友元
        • 2. 类做友元 (中有说明`class::名称`的说明)
        • 3. 成员函数做友元
      • 运算符重载
        • 加号运算符重载
        • 左移运算符重载
        • 递增运算符重载
        • 赋值运算符重载
        • 关系运算符重载
        • 函数调用运算符重载
      • 继承(重点)
        • 继承方式
        • 继承中的对象模型
        • 构造和析构顺序(继承中)
        • 同名成员处理
        • 同名静态成员处理(继承中)
        • 多继承语法
        • 棱形继承
          • 问题
      • 多态
        • 案例1: 开闭原则
        • 纯虚函数&抽象类
          • 抽象类特点

C++面向对象的三大特性为: 封装继承多态

封装

封装的意义

  • 将属性和行为作为一个整体,表现生活中的事物
  • 将属性和行为加以权限控制

封装的意义

在设计类的时候,属性和行为写在一起,表现事物

语法: class 类名{访问权限:属性/行为}

案例1

  • 设计一个圆类,求圆的周长
#include 

using namespace std;

const double PI = 3.14;
class Circle {
	//访问权限
public:
	//属性
	int m_r;//半径
	//行为
	//获取圆的周长
	double calculateZC() {
		return 2 * PI * m_r;
	}
};

int main() {
	//通过圆类创建具体的圆(对象)
	Circle c1;
	//给圆类型对象进行赋值
	c1.m_r = 10;
	cout << "圆的周长为:" << c1.calculateZC() << endl;
	return 0;
}

C++学习之旅 -类和对象(重点)_第1张图片

案例2

设计一个学生类,属性有名字学号

#include 

using namespace std;

class Student {
public:
	int sid;
	string name;

	void showStudent() {
		cout << "学号:" << sid <<",姓名:" << name << endl;
	}
};

int main() {
	Student s1;
	s1.sid = 1;
	s1.name = "张三";
	s1.showStudent();
	return 0;
}

访问权限

  1. public: 公共
  2. protected: -保护权限
  3. private: 私有
#include 

using namespace std;

//public: 类内和类外都可以使用
//protected:类内可以访问,类外不可以访问;子类可以访问父类中的保护权限
//private: 类内可以访问,类外不可以访问;子类不可以访问父类中的私有内容
class Person {
	//公共权限
public:
	string m_Name;
	//保护权限
protected:
	string m_Car;
//私有权限
private:
	int m_PassWord;
public:
	void func() {
		m_Name = "张三";
		m_Car = "比亚迪";
		m_PassWord = 123456;
	}
};


int main() {
	Person p1;
	p1.m_Name = "李四";
	//p1.m_Car = "奔驰";//保护访问权限内容,在类外访问不到
	//p1.m_PassWord = 11223344;//私有权限内容,在类外访问不到
	return 0;
}

保护访问权限内容,在类外访问不到
C++学习之旅 -类和对象(重点)_第2张图片

C++中class和struct的区别

区别就在于默认的访问权限不同:

  • struct默认权限是公共
  • class默认权限是私有

成员属性私有化

在实际开发中都会将我们的成员属性私有化

  • 优点1: 将所有成员设置为私有,可以自己控制读写权限
  • 有点2: 对于写权限,可以检测数据的有效性

示例

#include 

using namespace std;

class Person {
private:
	int sid;
	string name;
	int age;
public:
	int getSid() {
		return this->sid;
	}
	void setSid(int sid) {
		this->sid = sid;
	}
	string getName() {
		return this->name;
	}
	void setName(string name) {
		this->name = name;
	}
	int getAge() {
		return this->age;
	}
	void setAge(int age) {
		this->age = age;
	}
};


int main() {
	Person p1;
	p1.setSid(1);
	p1.setName("张三");
	p1.setAge(18);
	cout << p1.getSid() << p1.getName() << p1.getAge() << endl;
	return 0;
}

构造函数和析构函数

构造函数

构造函数(对象的初始化状态)
语法类名(){}

  1. 构造函数,没有返回值也不写void
  2. 函数名与类名相同
  3. 构造函数可以有参数,所以可以发生重载
  4. 程序调用对象的时候会自动调用构造函数,无须手动调用,而且只会调用一次

析构函数

在对象销毁前编译器也会调用它
语法~类名(){}

  1. 析构函数,没有返回值不写void
  2. 函数名与类名相同,在前面加上符号~
  3. 析构函数不可以有参数,因此不可发生重载
  4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
#include 

using namespace std;

class Person {
private:
	string name;
public:  //注意要写作用域,目的是让类外访问
	Person() { 
      //不写会自己给你一个空实现,写了就用你自己的
    cout << "无参构造函数Person调用了" << endl; }
	Person(string name) {
		this->name = name;
	}
};


int main() {
	Person p1;
	return 0;
}

C++学习之旅 -类和对象(重点)_第3张图片

注意点: 没有写构造函数的时候默认调用空实现的无参构造函数,一旦写了构造函数,之后的构造函数也不存在执行(原意:自己写了个有参构造函数自己没写无参,无参构造函数就是没有的)

#include 

using namespace std;

class Person {
private:
	string name;
public:  //注意要写作用域,目的是让类外访问
	//构造函数
	Person() { cout << "无参构造函数Person调用了" << endl; }
	Person(string name) {
		this->name = name;
	}
	//析构函数
	~Person() {
		cout << "析构函数~Person调用了" << endl;
	}
};


int main() {
	Person p1; //这是在栈上的数据,main执行完后释放这个对象
	return 0;
}

C++学习之旅 -类和对象(重点)_第4张图片

构造和析构都是必须要有的,如果自己不写,那么编译器会提供一个空实现的构造和析构

构造函数的分类以及调用

俩种分类方法:

  • 按参数
    • 无参构造
    • 有参构造
  • 按类型
    • 普通构造
    • 拷贝构造
构造&调用
class Person{
  //无参构造函数
  Person(){/*自行添加*/}
  //有参构造函数
  Person(int a){/*自行添加*/}
  //拷贝构造函数
  Person(const int &p){/*自行添加*/}
  //析构函数
  ~Person(){/*自行添加*/}
};
//调用
int main(){
  //1.括号法
  Person p1; //无参,不需要学Person p1();有问题(这样会认为是一个函数声明)
  Person p2(10); //有参
  Person p3(p2);//拷贝
  //2. 显示法
  Person p4;//无参
  Person p5 = Person(10);//有参
  Person p6 = Person(p5);//拷贝
  Person(10);//匿名对象,特点:当前行执行结束后,系统会立即回收掉匿名对象
  
  //3.隐式转换法
  Person p7 = 10; //相当于写了Person p7 = Person(10)
  Person p8 = p7; //拷贝构造
}

注意: 不要利用拷贝构造函数初始化匿名对象,Person(p3)因为编译器会认为是一个对象的声明

拷贝构造函数调用时机

  • 使用一个已经创建完毕的对象来初始化一个新对象
#include 

using namespace std;

class Person {
public:
	int age;
public:
	Person() {//无参构造
		cout << "无参构造函数调用" << endl;
	}
	Person(int age) { //有参构造
		this->age = age;
		cout << "有参构造函数调用" << endl;
	}
	Person(const Person& p) {//拷贝构造
		this->age = p.age;
		cout << "拷贝构造函数调用" << endl;
	}
	~Person() { //析构函数
		cout << "析构函数调用" << endl;
	}
};


int main() {
	Person p1(20); 
	Person p2(p1);
	cout << "p2的年龄:" << p2.age << endl;//查看是否拷贝出来
	return 0;
}

C++学习之旅 -类和对象(重点)_第5张图片

  • 值传递的方式给函数传值
#include 

using namespace std;

class Person {
public :
	int age;
public:
	Person() {//无参构造
		cout << "无参构造函数调用" << endl;
	}
	Person(int age) { //有参构造
		this->age = age;
		cout << "有参构造函数调用" << endl;
	}
	Person(const Person& p) {//拷贝构造
		this->age = p.age;
		cout << "拷贝构造函数调用" << endl;
	}
	~Person() { //析构函数
		cout << "析构函数调用" << endl;
	}
};

void doWork(Person p) {
  
}

int main() {
	Person p;
	doWork(p);
	return 0;
}

C++学习之旅 -类和对象(重点)_第6张图片

  • 以值方式返回局部对象
#include 

using namespace std;

class Person {
public :
	int age;
public:
	Person() {//无参构造
		cout << "无参构造函数调用" << endl;
	}
	Person(int age) { //有参构造
		this->age = age;
		cout << "有参构造函数调用" << endl;
	}
	Person(const Person& p) {//拷贝构造
		this->age = p.age;
		cout << "拷贝构造函数调用" << endl;
	}
	~Person() { //析构函数
		cout << "析构函数调用" << endl;
	}
};

Person doWork() {
	Person p1;
	cout << (int*)&p1 << endl;
	return p1;
}

int main() {
	Person p = doWork();
	cout << (int*)&p << endl;
	return 0;
}

C++学习之旅 -类和对象(重点)_第7张图片

其实应该是不一样的地址,这里编译器优化了

深拷贝&浅拷贝

  • 浅拷贝: 简单的赋值拷贝操作
  • 深拷贝: 在堆区重新申请空间,进行拷贝操作
#include 

using namespace std;

class Person {
public :
	int age;
	int* m_Height;
public:
	Person() {//无参构造
		cout << "无参构造函数调用" << endl;
	}
	Person(int age,int height) { //有参构造
		this->age = age;
		m_Height = new int(height); //指针接收堆区的数据
		cout << "有参构造函数调用" << endl;
	}
	Person(const Person& p) {//拷贝构造
		this->age = p.age;
		cout << "拷贝构造函数调用" << endl;
	}
	~Person() { //析构函数
		if (m_Height == NULL) {
			delete m_Height;
			m_Height = NULL;
		}
		cout << "析构函数调用" << endl;
	}
};


int main() {
	Person p1(18,160);
	cout << "p1的年龄:" << p1.age << "p1身高:" << *p1.m_Height << endl;
	Person p2(p1);
	cout << "p2的年龄:" << p2.age << "p2身高:" << *p2.m_Height << endl;
	return 0;
}

C++学习之旅 -类和对象(重点)_第8张图片

浅拷贝带来的问题,堆区资源重复释放

#include 

using namespace std;

class Person {
public :
	int age;
	int* m_Height;
public:
	Person() {//无参构造
		cout << "无参构造函数调用" << endl;
	}
	Person(int age,int height) { //有参构造
		this->age = age;
		m_Height = new int(height); //指针接收堆区的数据
		cout << "有参构造函数调用" << endl;
	}
	Person(const Person& p) {//拷贝构造
		this->age = p.age;
		m_Height = new int(*p.m_Height);
		cout << "拷贝构造函数调用" << endl;
	}
	~Person() { //析构函数
		if (m_Height == NULL) {
			delete m_Height;
			m_Height = NULL;
		}
		cout << "析构函数调用" << endl;
	}
};


int main() {
	Person p1(18,160);
	cout << "p1的年龄:" << p1.age << "p1身高:" << *p1.m_Height << endl;
	Person p2(p1);
	cout << "p2的年龄:" << p2.age << "p2身高:" << *p2.m_Height << endl;
	return 0;
}

初始化列表

作用: 用来初始化属性
语法: 构造函数(): 属性1(值1),属性2(值2),...{}

#include 

using namespace std;

class Person {
public:
	int m_A;
	int m_B;
	int m_C;
	Person():m_A(10),m_B(20),m_C(30) {}
	//不是写死的
	//Person(int a,int b,int c):m_A(a),m_B(b),m_C(c) {}
};


int main() {
	Person p;
	cout << p.m_A << endl;
}

类对象作为类成员

#include 

using namespace std;
class Phone {
public:
	string p_name;
	Phone(string p_name) :p_name(p_name) {}
};
class Person {
public:
	string name;
	Phone phone;
	//Phone phone = phoneName;
	Person(string name, string phoneName):name(name),phone(phoneName) {
	}
};


int main() {
	Person p("张三", "苹果XR");
	cout << p.name << "拿着" << p.phone.p_name << endl;
}

静态成员

  • 静态成员变量
    • 所有对象共享一份数据
    • 在编译阶段分配内存
    • 类内声明;类外初始化
      C++学习之旅 -类和对象(重点)_第9张图片
#include 

using namespace std;

class Person {
public:
	//所有对象都共享一份
	//类内声明
	static int m_A;
};
//类外初始化
int Person::m_A = 100;

int main() {
	Person p;
	cout << p.m_A << endl;
}

静态成员不属于某个对象上,所有对象都共享一份数据
静态成员有俩种访问方式

  1. 通过对象进行访问
Person p;
cout << p.m_A << endl;
  1. 通过类名进行访问
//类名::静态变量名
cout << Person::m_A << endl;

静态成员变量也是有访问权限的

class 类名{
  private:
    static int m_B;
}
//int Person::m_B = 200;//不可以访问,原因是private私有的
  • 静态成员函数
    • 所有对象共享同一函数
#include 

using namespace std;

class Person {
public:
	static void func() {
		cout << "static void func 调用" << endl;
	}
};


int main() {
	//俩种方法方式
	//1.通过对象访问
	Person p;
	p.func();
	//2.通过类名进行访问
	Person::func();
}
  • 静态成员函数只能访问静态成员变量
#include 

using namespace std;

class Person {
public:
	static int m_A;
	static void func() {
		m_A = 300;//静态成员函数可以访问静态成员变量
		cout << "static void func 调用" << endl;
	}
};

int Person::m_A = 100;

int main() {
	//俩种方法方式
	//1.通过对象访问
	Person p;
	p.func();
	//2.通过类名进行访问
	Person::func();
}

非静态成员变量是通过实例来创建对象,所以静态函数不清楚非静态成员变量的变化

C++对象模型&this指针

成员变量和成员函数分开存储

空对象占用内存空间为: 1。C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置

#include 

using namespace std;

class Person {
public:
	int m_A; //非静态成员变量 (属于类的对象上)
	static int m_B; //静态成员变量(不属于类的对象上)

	void func() {} //非静态成员函数(不属于类的对象上)
	static void func2() {} //静态成员函数(不属于类的对象上)

};

int Person::m_B = 0;



int main() {
	Person p;
	cout << sizeof(p) << endl;
}

this指针

我们知道了成员变量和成员函数是分开存储的
每一个非静态成员只会诞生一份函数实例,也就是多个同类型的对象公用一块代码
那么问题是: 这一块代码是如何区分那个对象调用自己

this指针指向被调用的成员函数所属的对象

class Person{
  private:
    int money;
  public:
    Person(int money){
      this->money = money;
    }
}

空指针访问成员函数

#include 

using namespace std;

class Person {
public:
	int m_Age;
	void showClassName() {
		cout << "this is Person class" << endl;
	}
	void showPersonAge() {
		cout << "age = " << m_Age << endl;
	}
};




int main() {
	Person* p = NULL;
	p->showClassName();  //正常输出
	p->showPersonAge();
}

C++学习之旅 -类和对象(重点)_第10张图片

原因是this是空指针。在m_Age这段中,其实默认添加了this->m_Age
我们可以

class Person {
public:
	int m_Age;
	void showClassName() {
		cout << "this is Person class" << endl;
	}
	void showPersonAge() {
		if (this == NULL) {
			return;
		}
		cout << "age = " << m_Age << endl;
	}
};

const修饰成员函数

常函数

  • 成员函数后加const后我们称这个函数为常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改
#include 

using namespace std;

class Person {
public:
	int m_A;
	mutable int m_B; //特殊变量,即使在常函数中,也可以修改这个值
	//this指针的本质 是指针常量 指针的指向是不可以修改的
	void showPerson() const{ //这个const就相当于时const Person *const this;
		//this->m_A = 100; //Person *const this;表达式是必须可修改的左值
		//this = NULL;//this指针不能修改指针指向的
		this->m_B = 100;
	}
};
int main() {
	Person p;
	//p->showPersonAge();
}

常函数(void showPerson const)本质是const 对象名 *const this。要修改就要加上mutable关键字

常对象

  • 声明对象加const称该对象为常对象
  • 常对象只能调用常函数
#include 

using namespace std;

class Person {
public:
	int m_A;
	mutable int m_B; //特殊变量,即使在常函数中,也可以修改这个值
	//this指针的本质 是指针常量 指针的指向是不可以修改的
	void showPerson() const{ //这个const就相当于时const Person *const this;
		//this->m_A = 100; //Person *const this;表达式是必须可修改的左值
		//this = NULL;//this指针不能修改指针指向的
		this->m_B = 100;
	}
};




int main() {
	const Person p;
	//p.m_A = 10;//不能修改
	p.m_B = 20;//可以修改
	//p->showPersonAge();
}
//常对象只能调用常函数

友元

声明一些特殊的函数或者特殊的类来作为另一个类的朋友访问到这类的私有成员
友元关键字friend

友元的三种实现

1. 全局函数做友元

//没有友元
#include 

using namespace std;

//建筑物类
class Building {
public:
	string m_SittingRoom;//客厅
private:
	string m_BedRoom;//卧室
public:
	Building() {
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}
};
//全局函数
void goodFriend(Building *building) {
	cout << "好朋友的全局函数:" << building->m_SittingRoom << endl;  //公共属性
	//cout << "好朋友的自己的函数:" << building->m_BedRoom << endl; //报错
}




int main() {
	Building building;
	goodFriend(&building);
}
#include 

using namespace std;

//建筑物类
class Building {
	//goodFriend是这个Building的好朋友,可以访问Building中私有属性
	friend void goodFriend(Building* building);
public:
	string m_SittingRoom;//客厅
private:
	string m_BedRoom;//卧室
public:
	Building() {
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}
};
//全局函数
void goodFriend(Building *building) {
	cout << "好朋友的全局函数:" << building->m_SittingRoom << endl;  //公共属性
	cout << "好朋友的自己的函数:" << building->m_BedRoom << endl;
}




int main() {
	Building building;
	goodFriend(&building);
}

C++学习之旅 -类和对象(重点)_第11张图片

只要写在类的最上面就可以了,不需要什么public
核心: friend 返回值数据类型 函数名(形参);

2. 类做友元 (中有说明class::名称的说明)

//class访问公共属性
#include 

using namespace std;

class Building;//类声明
class GoodFriend {
public:
	Building* building;
	void visit(); //参观函数访问Building中的属性
	GoodFriend();
};

//建筑物类
class Building {
public:
	string m_SittingRoom;//客厅
private:
	string m_BedRoom;//卧室
public:
	Building();
};

//类外去写成员函数
Building::Building() {
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}
GoodFriend::GoodFriend() {
  //创建建筑物对象
	building = new Building;
}
void GoodFriend::visit() {
	cout << "好朋友正在访问" << building->m_SittingRoom << endl;
}

int main() {
	Building building;
	GoodFriend goodFriend;
	goodFriend.visit();
	
}
#include 

using namespace std;

class Building;//类声明
class GoodFriend {
public:
	Building* building;
	void visit(); //参观函数访问Building中的属性
	GoodFriend();
};

//建筑物类
class Building {
	friend class GoodFriend; //GoodFriend是本类的好朋友,可以访问本类中私有成员(主要)
public:
	string m_SittingRoom;//客厅
private:
	string m_BedRoom;//卧室
public:
	Building();
};

//类外去写成员函数
Building::Building() {
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}
GoodFriend::GoodFriend() {
  //创建建筑物对象
	building = new Building;
}
void GoodFriend::visit() {
	cout << "好朋友正在访问" << building->m_SittingRoom << endl;
	cout << "好朋友正在访问" << building->m_BedRoom << endl;
}

int main() {
	Building building;
	GoodFriend goodFriend;
	goodFriend.visit();
	
}

核心: friend class 类名

这里提到的一个写法知识点

class 类名{
public:
  数据类型 成员变量名;
  类名();
  返回值数据类型 成员函数名();
}
//初始化(构造函数)
类名::类名(){/*初始化*/}
//初始化(成员函数)
返回值数据类型 类名::成员函数(/*形参*/){/*内容*/}

3. 成员函数做友元

class 类名1{
  void 成员函数();
}
class 类名2{
  friend 类名1::成员函数(); //类1下的成员函数作为本类的好朋友,可以访问私有成员
private:
   int test;
}

运算符重载

加号运算符重载

//通过成员函数重载+运算符
#include 

using namespace std;

class Person {
public:
	int m_A;
	int m_B;
	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;
	}
};

int main() {
	Person p1;
	p1.m_A = 10;
	p1.m_B = 10;
	Person p2;
	p2.m_A = 20;
	p2.m_B = 20;

	Person p3 = p1 + p2;
	cout << "p2.m_A = " << p3.m_A << endl;
	cout << "p2.m_B = " << p3.m_B << endl;
	
}

成员函数的本质:Person p3 = p1.operator+(p1);
全局函数重载本质调用: Person p3 = operator+(p1,p2)

左移运算符重载

ostream operator<<(ostream &cout,对象数据类型 &名字){
  cout << "自定义" <<"自定义";
}
ostream &operator<<(ostream &cout,对象数据类型 &名字);

递增运算符重载

void operator++(){
  //先做运算
  //再返回
  return ...;
}

赋值运算符重载

c++编译器至少给一个类添加4个函数

  • 默认构造函数(无参,函数体为空)
  • 默认析构函数(无参,函数体为空)
  • 默认拷贝构造函数,对属性进行值拷贝
  • 赋值运算符operator=对属性进行值拷贝
void operator=(){
  if(m_Age != NULL){
    delete m_Age;
    m_Age = NULL;
  }
}

关系运算符重载

void operator==(){}
void operator!=(){}

函数调用运算符重载

//仿函数没有固定的写法
void operator()(string test){
  cout << test <<endl;
}
int operator()(string test,int age){}

继承(重点)

语法class 子类: 继承方式 父类

继承方式

  1. 公共继承
class A{
  //父类
public:
  int m_A;
protected:
  int m_B;
private:
  int m_C;
};
class B:public A{
  void func(){
    m_A = 10;//父类中的公共权限成员到子类依旧是公共权限
    m_B = 10;//父类中的保护权限成员到子类依旧是保护权限
    //m_C = 10;//父类中的隐私权限成员到子类中是拿不到私有权限
  }
};
  1. 保护继承
class A{
  //父类
public:
  int m_A;
protected:
  int m_B;
private:
  int m_C;
};
class B:protected A{
public:
  void func(){
    m_A = 10;//父类中的公共权限成员到子类中变为了保护权限
    m_B = 10;//父类中的保护权限成员到子类依旧是保护权限
    //m_C = 10;//父类中的隐私权限成员到子类中是拿不到私有权限
  }
};
  1. 私有继承
class A{
  //父类
public:
  int m_A;
protected:
  int m_B;
private:
  int m_C;
};
class B:private A{
public:
  void func(){
    m_A = 10;//父类中的公共权限成员到子类中变为了私有成员
    m_B = 10;//父类中的保护权限成员到子类中变为了私有成员
    //m_C = 10;//父类中的隐私权限成员到子类中是拿不到私有权限
  }
};

继承中的对象模型

class A{
  //父类
public:
  int m_A;
protected:
  int m_B;
private:
  int m_C;
};
class B:public A{
public:
  int m_D;
};
void test(){
  cout << "sizeof B=" << sizeof(B) << endl;
}

C++学习之旅 -类和对象(重点)_第12张图片

父类中非静态是私有还是公共权限的数据我们子类都会被继承下来保留一份(自己中的属性也包括)

验证:打开图中高亮的文件

C++学习之旅 -类和对象(重点)_第13张图片

注意: 文件路径不能有&

#cl /d1 报告单个类的布局(reportSingleClassLayout类名) 类在的.cpp文件
cl /d1 reportSingleClassLayoutB main.cpp

C++学习之旅 -类和对象(重点)_第14张图片

构造和析构顺序(继承中)

#include 

using namespace std;

class A {
	//父类
public:
	int m_A;
	A() {
		cout << "父类A构造函数" << endl;
	}
	~A() {
		cout << "父类A析构函数" << endl;
	}
protected:
	int m_B;
private:
	int m_C;
};
class B :public A {
public:
	int m_D;
	B() {
		cout << "子类B构造函数" << endl;
	}
	~B() {
		cout << "子类B析构函数" << endl;
	}
};
void test() {
	cout << "sizeof B=" << sizeof(B) << endl;
}

int main() {
	B b;
	return 0;
}

C++学习之旅 -类和对象(重点)_第15张图片

同名成员处理

问题: 父类和子类出现了同样名称的成员

  • 访问子类同名成员,直接访问即可
#include 

using namespace std;

class A {
	//父类
public:
	int m_A;
	A() {
		m_A = 100;
		cout << "父类A构造函数" << endl;
	}
};
class B :public A {
public:
	int m_A;
	B() {
		m_A = 200;
		cout << "子类B构造函数" << endl;
	}
	void func(){}
};
void test() {
	B b;
	cout << "B类下的m_A=" << b.m_A << endl;
}

int main() {
	test();
	return 0;
}

C++学习之旅 -类和对象(重点)_第16张图片

  • 访问父类同名成员,需要加作用域
//子类实例对象名.父类名::同名成员变量/同名成员函数
void test() {
	B b;
	cout << "A类下的m_A=" << b.A::m_A << endl;
}

C++学习之旅 -类和对象(重点)_第17张图片

如果想访问到父类中被隐藏的成员函数,需要加作用域

子类实例名.类名::函数名(/*形参*/);

同名静态成员处理(继承中)

#include 

using namespace std;

class Base {
public:
	static int m_A;
	static void func() {
		cout << "Base func" << endl;
	}
};
int Base::m_A = 100;

class Son:public Base{
public:
	static int m_A;
	static void func() {
		cout << "Son func" << endl;
	}
};
int Son::m_A = 200;

int main() {
	//同名静态成员属性
	Son s;
	//1.通过对象的方式访问
	cout << "通过对象的方式访问" << endl;
	cout << "子类s的静态变量=" << s.m_A << endl;
	cout << "父类Base下的静态变量=" << s.Base::m_A << endl;
	//2.通过类名的方式访问
	cout << "通过类名的方式访问" << endl;
	cout << "子类s的静态变量=" << Son::m_A << endl;
	cout << "父类Base下的静态变量=" << Son::Base::m_A << endl;

	//同名静态成员函数
	cout << "通过对象的方式访问" << endl;
	s.func();
	s.Base::func();
	cout << "通过类名的方式访问" << endl;
	Son::func();
	Son::Base::func();
	return 0;
}

多继承语法

在C++中可以继承多个类
语法: class 子类:继承方式 父类1,继承方式 父类2...

因为可能存在俩个父类中存在成员重名问题,所以实际开发中不建议使用多继承

#include 

using namespace std;

class Base1 {
public:
	int m_A;
	int m_D;
	Base1() {
		m_A = 100;
	}
};
class Base2 {
public:
	int m_B;
	int m_D;
	Base2() {
		m_B = 200;
	}
};

class Son :public Base1, public Base2 {
public:
	int m_C;
};

int main() {
	Son s;
	cout << "sizeof Son=" << sizeof(s) << endl;
	cout << "Base1 m_D=" << s.Base1::m_D << endl;
	cout << "Base2 m_D=" << s.Base2::m_D << endl;
	return 0;
}

棱形继承

俩个派生类继承一个基类(人话:俩个子类一个父类);之后一个类继承了这俩个派生类

问题
  1. B类继承了A类,C类也继承了A类,D类继承了B和C类;当D类使用数据时,就会产生二义性
  2. D类继承了B和C类,其实我们都清楚我们只需要一份就可以了
#include 

using namespace std;

class Anime {
public:
	int m_Age;
};
class Sheep :public Anime {
};
class Tuo :public Anime {
};
class SheepTuo :public Sheep, public Tuo {};

int main() {
	SheepTuo st;
	st.Sheep::m_Age = 18;
	st.Tuo::m_Age = 20;
	//当出现菱形继承,俩个父类拥有相同的数据,需要加以作用域区分
	cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
	cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;
	return 0;
}

解决: 继承之前加上关键字virtual变成虚继承

#include 

using namespace std;

class Anime {
public:
	int m_Age;
};
class Sheep :virtual public Anime {
};
class Tuo :virtual public Anime {
};
class SheepTuo :public Sheep, public Tuo {};

int main() {
	SheepTuo st;
	st.Sheep::m_Age = 18;
	st.Tuo::m_Age = 20;
	//当出现菱形继承,俩个父类拥有相同的数据,需要加以作用域区分
	cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
	cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;
	return 0;
}

多态

  • 静态多态
    • 函数重载
    • 运算符重载
    • 服用函数名
  • 动态多态
    • 派生类
    • 虚函数实现运行时多态
#include 

using namespace std;

class Anime {
public:
	void speak() {
		cout << "动物在说话" << endl;
	}
};

class Cat :public Anime {
public:
	void speak() {
		cout << "猫在说话" << endl;
	}
};

//执行说话函数
void doSpeak(Anime& anime) { //父类的引用指向子类对象Anime &anime = cat
	anime.speak();
}


int main() {
	Cat cat;
	doSpeak(cat);
	return 0;
}

输出的结果是动物在说话,这个时候就要使用虚函数

class Anime {
public:
	//虚函数
	virtual void speak() {
		cout << "动物在说话" << endl;
	}
};
//动态多态的满足条件
//1.有继承关系
//2.子类要重写父类的虚函数
  • 动态多态使用

    • 父类的指针或者引用 执行子类的对象
  • 多态的优点

    • 代码组织结构清晰
    • 可读性强
    • 利于前期和后期的扩展以及维护

案例1: 开闭原则

class AbstractCalculator{
  public:
    virtual int getResult(){
      return 0;
    }
    int num1;
    int num2;
};
//加法计算器类
class AddCalulator:public AbstractCalculator{
public:
  int getResult(){
    return num1+num2;
  }
};
//乘法计算器类
class MulCalulator:public AbstractCalculator{
public:
  int getResult(){
    return num1 * num2;
  }
};

int main(){
  AbstractCalculator *add = new AddCalulator();
  add->num1 = 10;
  add->num2 = 20;
  
}

纯虚函数&抽象类

以案例1来看AbstractCalculator中的虚函数getResultreturn 0一直都没使用过,所以我们可以改为纯虚函数
语法: virtual 返回值类型 函数名(参数列表) = 0;

当类中有了纯虚函数,这个类也被称为抽象类

抽象类特点
  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于是抽象类
#include 

using namespace std;

//Base是抽象类
class Base {
public:
	//只要有一个纯虚函数,这个类就是抽象类
	//抽象类无法实例化对象
	virtual void func() = 0;
};

class Son :public Base {
public:
	void func() {
		cout << "Son func" << endl;
	}
};



int main() {
	Base* base = new Son;
	base->func();
	return 0;
}

你可能感兴趣的:(C/C++,c++,学习,开发语言)