类和对象的学习

对象特性

this指针

成员变量和成员函数是分开存储的,每一个非静态成员函数只会有一个函数实例,也就是多个同类型的对象公用一块代码
C++通过this指针区分是哪个对象调用了这个函数
this指针是隐含在每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用就行

this指针的本质时一个指针常量

this指针的用途

在形参和成员变量同名时,可以用this指针区分
在类的非静态成员函数中返回对象本身,可以使用return *this
class Person {
public:
	// this区分不同的属性
	Person(int age) {
		this->age = age;
	}
	// 返回对象本身
	Person PersonAddage(Person p) {
		return *this;
	}

	string name;
	int age;
};

空指针访问成员函数

class Person {
public:
	// this区分不同的属性
	Person(int age) {
		this->age = age;
	}
	// 返回对象本身
	void showPerson() {
		cout << age << endl;
	}

	void showPerson2() {
		cout << this->age << endl;
	}
	void showPerson3() {
		if (this == NULL) {
			return;		// 解决空指针访问报错问题
		}
		cout << this->age << endl;
	}
	string name;
	int age;
};

int main() {
	Person* p = NULL;
	p->showPerson();		// 不会报错
	p->showPerson2();		// 存在this指针访问成员,会报错
	p->showPerson3();		// 不会报错
}

const修饰成员函数

常函数:

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

常对象:

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

友元

友元:让一个函数或者类访问另一个类中的私有成员
友元的三种实现:

全局函数做友元
类做友元
成员函数做友元

全局函数做友元

class Person {
	//让全局函数boy函数能够访问Person中的私有成员
	friend void boy(Person *p);
public:
	int score;
private:
	string name;
	int age;
};

void boy(Person *p) {
}

类做友元

class Person {
	// boy类可以访问本类的私有成员
	friend class boy;
public:
	int score;
private:
	string name;
	int age;
};
class boy {
};

成员函数做友元

class Person {
	// boy类的A成员函数可以访问本类的私有成员
	friend void boy::A();
public:
	int score;
private:
	string name;
	int age;
};
class boy {
public:
	void A() {

	}
};

继承

继承的基础语法

class Person {
public:
	int score;
private:
	string name;
	int age;
};

//  boy继承Person
class boy:public Person {
public:
	void A() {

	}
};

继承方式

公共继承 public:父类中公共和保护权限都不变
公共继承 protected:父类中公共和保护权限都变成保护权限
公共继承 private:父类中公共和保护权限都变成私有权限
父类中的私有成员在继承时会被子类继承,但是会被编译器隐藏,无法访问
父类中的非静态成员都会被继承

继承中的构造和析构顺序:
先构造父类在构造子类
先析构子类再析构父类

继承中同名成员以子类为主
访问父类的成员用作用域

class Person {
public:
	int score = 10;
};
class Person1 {
public:
	int score=20;
};
//  boy继承Person
class boy:public Person, public Person1{
public:
	int score=20;
};
int main() {
	boy b;
	// 访问子类的成员
	cout << b.score << endl;
	// 访问父类的成员
	cout << b.Person::score << endl;
}

多态

静态多态:函数重载和运算符重载 属于静态多态,复用函数名
动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态的区别

静态多态的函数地址早绑定——编译阶段确定函数地址
动态多态的函数地址晚绑定——运行阶段确定函数地址

多态的优点

代码组织结构清晰
可读性强
利于前期和后期的扩展以及维护
class Person {
public:
	void speak() {

	}
	virtual void eat() {

	}

};

//  boy继承Person
class boy:public Person{
public:
	void speak() {
	}
	//  地址晚绑定,
	void eat() {
	}
};
int main() {
	// 地址早绑定,在编译阶段绑定,执行时Person的方法生效
	//virtual修饰的为地址晚绑定,调用时boy的方法生效
	//子类要重写父类的虚函数
	//重写:函数的返回值类型,函数名,参数列表完全一样
	Person p = boy();
}

纯虚函数和抽象类

多态中,父类中的虚函数是毫无意义的,主要都是调用子类重写的内容,可以把虚函数写成纯虚函数
纯虚函数:virtual 返回值类型 函数名(参数列表)=0;
当类中存在纯虚函数,这个类成为抽象类

抽象类的特点

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

虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构的共性

可以解决父类指针释放子类对象
都需要有具体的函数实现

虚析构和纯虚析构的区别

如果是纯虚析构,该类属于抽象类,无法实例化对象
不是所有类都要虚析构或者纯虚析构,不需要执行子类的虚构的程序就不需要

class Person {
public:
// 析构函数如果不是虚析构,父类指针在虚构的时候不会调用子类中虚构函数,导致子类如果有堆区属性出现内存泄露
virtual ~Person() {

}
// 纯虚析构,纯虚析构要实现
virtual ~Person() = 0;

};
// 纯虚析构的实现
Person::~Person() {

}
// boy继承Person
class boy:public Person{
public:
boy(string name) {
// 创建一个指针指向堆中数据
p_name = new string(name);
}
string* p_name;
~boy() {

}

};

你可能感兴趣的:(c++,c++)