c++教程四类和对象

138 阅读5分钟

对象的初始化和清理

对象的初始化和清理
c++利用构造函数和析构函数
深拷贝:简单的赋值拷贝操作
浅拷贝:在堆区重新申请空间,进行拷贝操作
m_Height=p.m_Height
m_Height = new int(*p.m_Height);

初始化列表:

语法:构造函数():属性1(值1),属性2(值2),属性3(值3)...{}
示例:
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){}

类对象作为类成员

class A{}
class B{
	A a;
}

静态成员函数

所有对象共享同一个函数
静态成员函数只能访问静态成员变量
class Person{
public:
	//静态成员函数
    m_A=100;
    static void func(){
    	cout<<""<<end;
    }
    static int m_A;	//静态成员变量

}
int Person::m_A=0;	//静态成员变量,需要在类外初始化
void test01(){
	//第一种调用静态函数的方法
    Person p;
    p.func();
    //第二种通过类名访问
    Person::func();
}

c++对象模型和this指针

成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象

this指针概念

每一个非静态成员变量只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
C++通过提供特殊对象,this指针,解决上述问题,this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
this指针的用途:
当形参和成员函数同名时,可用this指针来区分
在类的非静态成员函数中返回对象本身,可使用return *this

class Person{
public:
	Person(int age){
    	this->age=age;
    }
    int age;
};
void test(){
	Person p1(18);
}

public:
	Person(int age){
    	this->age=age;
    }
    //链式编程思想
    Person& PersonAddAge(Person &p){
    	this->age += p.age;	// 
        return *this;	//返回本身
    }
    int age;
};
void test(){
	Person p1(10);
	Person p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
}


空指针访问成员函数

//空指针
class Person{
public:
	void showClassName(){
    	
    }
    void showPersonAge(){
    	
    }
    
};

void test01(){
	Person *p=NULL;
    p->showClassName();
    
}

const修饰成员函数

常函数:
成员函数加const后我们称为这个函数的常函数
常函数内不可以修改成员属性
成员属性声明时加关键字mutable,在常函数中依次可以修改
常对象:
声明对象前加const称为该对象为常函数
常对象只能调用常函数

this->m	//Person * const this;

mutable int m;

常对象:
void test02(){
	const Person p;	//在对象前加const,变为常对象
}

友元

friend void goodGay(Building *building);//写在类的最上面
friend class className;
做友元可以访问私有属性

运算符重载:

运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
对于内置的数据类型,编译器如何进行运算:
int a = 10;
int b = 10;
int c = a*b;



继承

语法:class 子类:public(继承方式) 父类{};
class BasePage{};
class Java:public BasePage{
public:
	
}
子类也叫派生类,父类也叫基类
protected继承可以访问
继承方式:
公共继承:public
保护继承:protected
私有继承:private


继承中对象模型

//父类中多有非静态成员都会被子类继承下去,只是访问不到
cl /d1 reportSingleClassLayoutSon name.cpp

继承中构造和析构顺序

顺序:父类构造,子类构造,子类析构,父类析构

继承同名成员处理方式

访问子类同名成员,直接访问即可
访问父类同名成员,需要加作用域: 父类::属性
作用域就是::
同名成员函数怎么处理
也是加作用域

继承中同名静态成员处理方式

静态和非静态出现同名,处理方式一样
访问子类同名,直接访问
访问父类同名成员,加作用域
{
public:
	static int m_A;
}
int Base::m_A=100;

多继承语法:

class 子类:继承方式 父类1,继承方式 父类2...
class Son:public Base1, public Base2{
public:
	Son()
}

菱形继承:

动物 羊	驼	羊驼
羊继承动物
驼继承动物
羊驼继承羊和驼	叫做菱形继承或者叫钻石继承

多继承会产生二义性
羊驼会继承两份,其实我们应该清楚,这份数据我们只需要一份就可以了

class Animal {
public:
	int m_Age;
};

//羊类
//利用虚继承可以解决菱形继承的问题
//在继承:后面加上virtual
//Animal类称为虚基类
//继承之前加上关键字,virtual变成虚继承
class Sheep:virtual public Animal {

};

//驼类
class Tuo :virtual public Animal {

};
//羊驼类
class SheepTuo :public Sheep, public Tuo {

};

全部变成了一个
void test02() {
	SheepTuo st;
	st.Sheep::m_Age=18;
	st.Tuo::m_Age = 28;
	//当出现菱形继承的时候,有两个父类拥有相同的数据,需要加以作用域区分
	cout << st.Sheep::m_Age << endl;	//全部变成28
	cout << st.Tuo::m_Age << endl;	//输出28
	//这份数据我们知道,只有一份就可以了,菱形继承导致数据有两份,资源浪费
	cout << st.m_Age << endl;	//输出28
}

vbptr:virtual base pointer 虚基类指针指向vbtable,指向一个虚基类表
记住的是指针,第二个指针继承第二个数字

多态

多态是c++面向对象三大特征之一
静态多态:函数重载和运算符重载属于静态多态,复合函数名
动态多态:派生类和虚函数实现运行时多态
区别:
静态多态的函数地址早绑定-编译阶段确定函数地址
动态多态的函数地址晚绑定-运行阶段确定函数地址

class Animal {
public:
	//虚函数
	virtual void speak() {
		cout << "动物在说话" << endl;
	}

};

//猫类
class Cat:public Animal {
public:
	void speak() {
		cout << "小猫在说话" << endl;
	}
};
//地址早绑定,在编译阶段确定函数地址,都会走animal的speak
//如果想让猫说话,这个函数的地址不能早绑定需要在运行中绑定
void doSpeak(Animal &animal) {	//Animal & animal=cat;
	animal.speak();
}

void test01() {
	Cat cat;
	doSpeak(cat);
}


int main() {

	test01();

动态多态满足条件:
1.有继承关系
2.子类要重写父类的虚函数
3.父类的指针或者引用,执行子类对象

多态深入解剖

动态多态:
静态多态:

vfptr:virtual function pointer:虚函数指针
虚函数地址:
&Animal::speak

//如果想扩展新的功能,需求修改源码
//在真的开发中,提倡开放原则
//开闭原则:对想扩展进行开发,对修改进行关闭

多态的好处:
组织结构清晰
易扩展

//利用多态实现计算器
//实现计算器抽象类
//可读性好
class AbstractCalculator{
public:

	virtual int getResult() {
		return 0;
	}

	int m_Num1;
	int m_Num2;
};

//设计一个加法计算器类
class AddCalculator :public AbstractCalculator {
public:
	int getResult() {
		return m_Num1 + m_Num2;
	}
};
//减法计算器类
class SubCalculator :public AbstractCalculator {
public:
	int getResult() {
		return m_Num1 - m_Num2;
	}
};

class MulCalculator :public AbstractCalculator {
public:
	int getResult() {
		return m_Num1 * m_Num2;
	}
};

void test02() {

	//多态使用条件
	//父类指针或者引用指向子类对象
	AbstractCalculator *abc= new AddCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->m_Num1 << "+" << abc->m_Num2 << "=" << abc->getResult() << endl;
	//用完销毁
	delete abc;
	//减法运算

}




纯虚函数和抽象类

在多态中,通常父类的虚函数的实现中毫无意义,主要是调用子类重写的内容
因此可以改写为纯虚函数
纯虚函数:virtual 返回值类型 函数名 (参数列表)=0;
单类中有纯虚函数,这个类也称抽象类
抽象类:
无法实例化对象
子类必须重写类中的纯虚函数,否则也属于抽象类


class Base {
public:
	//纯虚函数,
	//只有一个纯虚函数,这个类称为抽象类
	//抽象类无法实例化对象
	//抽象类的子类必须要重写父类中的纯虚函数,否则也属性抽象类
	virtual void func() = 0;

};

class Son :public Base {

public:
	virtual void func() {
		cout << "func函数调用" << endl;
	}

};

void test01() {
	Base *base = new Son;	//指针指向Son
	base->func();
	delete base;
}

int main() {

	test01();
    
    
    
制作饮品案例:
#include<iostream>

using namespace std;

//多态案例二
//制作饮品

class AbstractDrinking {
public:
	//煮水
	virtual void Boil() = 0;
	//冲泡
	virtual void Brew() = 0;
	//倒入
	virtual void PourInCup() = 0;
	//加入辅助的作料
	virtual void PutSomething() = 0;
	//制作饮品
	void makeDrink() {
		Boil();
		Brew();
		PourInCup();
		PutSomething();
	}
};

//制作咖啡
class Coffee :public AbstractDrinking {
public:
	//煮水
	virtual void Boil() {
		cout << "煮水" << endl;
	}
	//冲泡
	virtual void Brew() {
		cout << "冲泡咖啡" << endl;
	}
	//倒入
	virtual void PourInCup() {
		cout << "倒入杯中" << endl;
	}
	//加入辅助的作料
	virtual void PutSomething() {
		cout << "加入糖和牛奶" << endl;
	}
};

//制作茶叶
class Tea :public AbstractDrinking {
public:
	//煮水
	virtual void Boil() {
		cout << "煮水" << endl;
	}
	//冲泡
	virtual void Brew() {
		cout << "冲泡茶叶" << endl;
	}
	//倒入
	virtual void PourInCup() {
		cout << "倒入杯中" << endl;
	}
	//加入辅助的作料
	virtual void PutSomething() {
		cout << "加入茶叶" << endl;
	}
};

void doWork(AbstractDrinking *abs) {
	abs->makeDrink();
	delete abs;//释放
}

void test01() {
	//制作咖啡
	doWork(new Coffee);
	//制作茶叶
	doWork(new Tea);
}


int main() {
	test01();


	system("pause");
	return 0;
}



虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
可以解决父类指针释放子类对象
都需要有具体的函数实现
虚析构和纯虚析构区别:
如果是纯虚析构,该类属性抽象类,无法实例化对象
虚析构和纯虚析构的区别:
如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:
virtual ~类名(){}
纯虚析构语法
virtual ~类名() =0;

//虚析构和纯虚析构
class Animal {
public:
	Animal() {
		cout << "Animal构造函数" << endl;
	}
	//纯虚函数
	virtual void speak() = 0;
	//virtual ~Animal() {	//利用虚析构解决 父类指针释放子类对象时,不干净的问题
	//	cout << "Animal析构函数调用" << endl;
	//}
	virtual ~Animal() = 0;	//纯虚析构

};

Animal::~Animal() {}	//必须要声明,纯虚析构必须要有具体的实现
//有了纯虚析构之后,这个类也属于抽象类,无法实例化对象

class Cat :public Animal {
public:
	Cat(string name) {
		m_Name = new string(name);
	}
	virtual void speak() {
		cout <<*m_Name<< "小猫在说话" << endl;

	}
	~Cat() {
		cout << "Cat析构函数" << endl;
		if (m_Name != NULL) {
			delete m_Name;
			m_Name = NULL;
		}
	}
	string *m_Name;
};

void test01() {
	Animal *animal = new Cat("Tom");
	//父类指针在析构时候,不会调用子类中的析构函数,导致子类如果有堆区属性,出现内存泄漏情况
	animal->speak();
	delete animal;
}

int main() {

	test01();

	system("pause");
	return 0;
}

类和对象案例:

#include<iostream>

using namespace std;

//电脑组装
//CPU类
class CPU {
public:
	//抽象的计算函数
	virtual void calculate() = 0;


};

//显卡类
class VideoCard {
public:
	virtual void display() = 0;
};

//抽象内存条类
class Memory {
public:
	virtual void storage() = 0;
};

//电脑类
class Computer {
public:
	Computer(CPU *cpu, VideoCard*vc, Memory*mem) {
		m_cpu = cpu;
		m_vc = vc;
		m_mem = mem;
	}
	void work() {
		m_cpu->calculate();
		m_vc->display();
		m_mem->storage();

	}
	~Computer() {
		if (m_cpu != NULL) {
			delete m_cpu;
			m_cpu = NULL;
		}
		if (m_mem != NULL) {
			delete m_cpu;
			m_mem = NULL;
		}
		if (m_vc != NULL) {
			delete m_vc;
			m_vc = NULL;
		}
	}
private:
	CPU *m_cpu;
	VideoCard *m_vc;
	Memory *m_mem;


};

//具体的厂商
//Intel厂商
class IntelCPU :public CPU {
public:
	virtual void calculate() {
		cout << "Intel的CPU开始计算了!" << endl;
	}
};
class LenovoMemory :public Memory {
public:
	virtual void storage() {
		cout << "联想的内存开始计算" << endl;
	}
};

class IntelVideoCard :public VideoCard {
public:
	virtual void display() {
		cout << "Intel的显卡开始显示了!" << endl;
	}
};
//提供工作的函数

void test01() {
	CPU *intelCpu = new IntelCPU;
	VideoCard *intelCard = new IntelVideoCard;
	Memory *intelMem = new LenovoMemory;
	//创建第一台电脑
	Computer *computer1 = new Computer(intelCpu, intelCard, intelMem);
	computer1->work();
	delete computer1;
}

int main() {
	test01();
	
	system("pause");
	return 0;
}