IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++中的类和对象 -> 正文阅读

[C++知识库]C++中的类和对象

C++面向对象的三大特性为:封装、继承、多态。C++认为万事万物都皆为对象,对象上有其属性和行为。如人可以作为对象,属性有姓名、年龄、身高、体重等,行为有吃、喝、走、跑等。

1.封装

1.1 封装的意义

封装是C++面向对象的三大特性之一。其意义为:
1)将属性和行为作为一个整体:在设计类的时候,属性和行为写在一起,表现事务语法class 类名{ 访问权限 : 属性 / 行为 };

#include<iostream>
using namespace std;
const double PI = 3.14;
//设计一个圆类,求圆的周长
class circle {
public:
    circle(int r) {
        this->r = r;
    }
    double GetZC() {
        return 2 * r * PI;
    }
private:
    int r;
};
int main() {
    //创建一个具体的圆(对象),实例化
    circle c(10);
    cout << c.GetZC() << endl;
    return 0;
}

类中的属性和行为,统一称为成员;属性:成员属性、成员变量;行为:成员函数、成员方法。

2)将属性和行为加以权限控制,访问权限有三种:
i:public 公共权限:成员 类内可以访问,类外可以访问
ii:protected 保护权限:成员 类内可以访问,类外不可访问,子类可以访问父类中的保护内容
iii:private 私有权限:成员 类内可以访问,类外不可访问,子类不可以访问父类中的私有内容

#include<iostream>
#include<string>
using namespace std;
class person{
public:
    //公共权限
    string name;//姓名
protected:
    //保护权限
    string car;//汽车
private:
    //私有权限
    int password;//银行卡密码
public:
    //类内可以访问
    void func() {
        name = "张三";
        car = "拖拉机";
        password = 123456;
    }
};
int main() {
    //实例化具体对象
    person p;
    p.name = "李四";//可以访问
    //p.car = "奔驰";//保护权限内容,在类外访问不到
    //p.password = 456;//私有权限内容,类外访问不到
    return 0;
}

1.2 struct和class区别

在C++中struct和class唯一的区别就在于默认的访问权限不同,struct默认权限为公共,class默认权限为私有。

#include<iostream>
#include<string>
using namespace std;
class person{
    int ID;//默认为私有的。
};
struct MyStruct{
    int ID;//默认权限为公共
};
int main() {
    person p;
    //p.ID = 100;//默认为私有
    MyStruct m;
    m.ID = 100;//默认公共
    return 0;
}

1.3 成员属性设置为私有

优点:
1)将所有成员属性设置为私有,可以自己控制读写权限。

#include<iostream>
#include<string>
using namespace std;
class person{
public:
    //设置姓名
    void setname(string name) {
        this->name = name;
    }
    //获取姓名
    string getname() {
        return  name;
    }

private:
    string name;
};
int main() {
    person p;
    p.setname("zhinen");
    cout << p.getname() << endl;
    return 0;
}

2)对于写权限,可以检测数据的有效性。

#include<iostream>
#include<string>
using namespace std;
class person{
public:
    //设置姓名
    void setname(string name) {
        this->name = name;
    }
    //获取姓名
    string getname() {
        return  name;
    }
    //设置年龄
    void setage(int age) {
        if (age >= 0 && age <= 100) {
            this->age = age;
        }
        else {
            cout << "年龄设置错误!已默认为0。" << endl;
            this->age = 0;
        }
    }
    //获取年龄
    int getage() {
        return age;
    }

private:
    string name;
    int age;
};
int main() {
    person p;
    p.setname("zhinen");
    cout << p.getname() << endl;
    p.setage(200);
    cout << p.getage() << endl;
    p.setage(60);
    cout << p.getage() << endl;
    return 0;
}

2.对象的初始化和清理

2.1 构造函数和析构函数

C++中的构造函数和析构函数会被编译器自动调用,完成对象的初始化和清理工作。如果我们不提供构造和析构函数,编译器会提供编译器提供的构造函数和析构函数是空实现。
构造函数:在创建对象时为对象的成员属性赋值,构造函数自动调用,无需手动调用
析构函数:在对象销毁前系统自动调用,执行一些清理工作。
构造函数语法:类名 (){}
1)没有返回值也不写void;
2)函数名称和类名相同;
3)构造函数可以有参数,因此可以发生重载;
4)程序在调用对象时会自动调用构造函数,无需手动调用而且只会调用一次。
析构函数语法:~类名 (){}
1)没有返回值也不写void;
2)函数名称和类名相同,在名称前面加上~;
3)构造函数不可以有参数,因此不可以发生重载;
4)程序在对象销毁前时会自动调用析构函数,无需手动调用而且只会调用一次。

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	//构造函数
	person() {
		cout << "person" << endl;
	}
	//析构函数
	~person() {
		cout << "~person" << endl;
	}
};
int main() {
	person p;//在栈上的数据,程序执行完,会释放这个对象
    return 0;
}

2.2 构造函数的分类及调用

两种分类方式:
1)按参数分为:有参构造和无参构造(默认构造)
2)按类型分为:普通构造和拷贝构造
三种调用方式:
1)括号法
2)显示法
3)隐式转换法

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	//构造函数
	//有参和无参
	person() {
		cout << "person()" << endl;
	}
	person(int a) {
		age = a;
		cout << "person(int a)" << endl;
	}
	//拷贝构造函数
	person(const person &p) {
		age = p.age;
		cout << "person(person p)" << endl;
	}
private:
	int age;
};
int main() {
	//括号法
	person p;//调用无参
	//如果person p();会被认为是一个函数声明,不会认为在创建对象
	person h(10);//调用有参
	person q(p);//调用拷贝构造函数
	//显示法
	person p1;
	person p2 = person(10);//person(10)为匿名对象,特点:当前行执行结束后,系统立即会受掉匿名对象
	person p3 = person(p2);
	//person(p2);
	//不要利用拷贝构造函数,初始化匿名对象,编译器会认为person(p2)===person p2
	//隐式转换法
	person p4;
	person p5 = 10;//==person p5 = person(10);
	person p6 = p5;
    return 0;
}

2.3 拷贝构造函数调用时机

C++中拷贝函数调用时机通常有三种情况:
1)使用一个已经创建完毕的对象来初始化一个新对象
2)值传递的方式给函数参数传值
3)以值返回局部对象

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	//构造函数
	//有参和无参
	person() {
		age = 0;
		cout << "person()" << endl;
	}
	person(int a) {
		age = a;
		cout << "person(int a)" << endl;
	}
	//拷贝构造函数
	person(const person &p) {
		age = p.age;
		cout << "person(const person & p)" << endl;
	}
	int getage() {
		return age;
	}
private:
	int age;
};
void fun(person) {

}
person fun2() {
	person p;
	return p;
}
int main() {
	//1)使用一个已经创建完毕的对象来初始化一个新对象
	person p1(10);
	person p2(p1);
	cout << p2.getage() << endl;
	//2)值传递的方式给函数参数传值
	person p3;
	fun(p3);//调用了拷贝构造函数
	//3)以值返回局部对象
	person p4 = fun2();
	return 0;
}

2.4 构造函数调用规则

默认情况下,c++编译器至少给一个类添加3个函数
1)默认构造函数(无参,函数体为空)
2)默认析构函数(无参,函数体为空)
3)默认拷贝函数,对属性进行值拷贝
构造函数调用规则如下:
1)如果用户定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝函数

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	//构造函数
	//有参和无参
	/*person() {
		age = 0;
		cout << "person()" << endl;
	}*/
	person(int a) {
		age = a;
		cout << "person(int a)" << endl;
	}
	//拷贝构造函数
	/*person(const person &p) {
		age = p.age;
		cout << "person(const person & p)" << endl;
	}*/
	int getage() {
		return age;
	}
	~person() {
		cout << "~person()" << endl;
	}
private:
	int age;
};
int main() {
	//1)如果用户定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝函数
	//person p;//	错误	C2512	“person” : 没有合适的默认构造函数可用
	person p1(20);
	person p2(p1);
	cout << p2.getage() << endl;
	return 0;
}

2)如果用户定义拷贝构造函数,C++不会再提供其他构造函数

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	//构造函数
	//有参和无参
	/*person() {
		age = 0;
		cout << "person()" << endl;
	}*/
	/*person(int a) {
		age = a;
		cout << "person(int a)" << endl;
	}*/
	//拷贝构造函数
	person(const person &p) {
		age = p.age;
		cout << "person(const person & p)" << endl;
	}
	int getage() {
		return age;
	}
	~person() {
		cout << "~person()" << endl;
	}
private:
	int age;
};
int main() {
	//2)如果用户定义拷贝构造函数,C++不会再提供其他构造函数
	//person p;//	错误	C2512	“person” : 没有合适的默认构造函数可用
	//person p1(20);//错误(活动)	E0289	没有与参数列表匹配的构造函数
	return 0;
}

2.5 深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	person() {
		age = 0;
		cout << "person()" << endl;
	}
	person(int a,int h) {
		age = a;
		height = new int(h);
		cout << "person(int a,int h)" << endl;
	}
	person(const person &p) {
		age = p.age;
		//height = p.height;//编译器默认实现的代码
		height = new int(*p.height);
		cout << "person(const person & p)" << endl;
	}
	int getage() {
		return age;
	}
	int* getheight() {
		return height;
	}
	~person() {
		//析构函数,将堆区开辟的数据做释放操作
		//出错,浅拷贝带来的问题:堆区的内存重复释放,
		//要利用深拷贝进行解决,需重新写拷贝构造函数,解决浅拷贝带来的问题
		if (height != NULL) {
			delete height;
			height = NULL;
		}
		cout << "~person()" << endl;
	}
private:
	int age;
	int* height;
};
int main() {
	person p(18,160);
	cout << p.getage() << " " << *p.getheight() << endl;
	person p1(p);
	cout << p1.getage() << " " << *p1.getheight() << endl;
	return 0;
}

2.6 初始化列表

C++提供了初始化列表语法,用来初始化属性
语法:构造函数(): 属性1(值1), 属性2(值2)…{}

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	//传统初始化的方式
	/*person(int a,int h) {
		age = a;
		height = h;
		cout << "person(int a,int h)" << endl;
	}*/
	//初始化列表初始化属性
	person(int a,int h) :age(a),height(h) {}
	int getage() {
		return age;
	}
	int getheight() {
		return height;
	}
	~person() {
		cout << "~person()" << endl;
	}
private:
	int age;
	int height;
};
int main() {
	person p(18,160);
	cout << p.getage() << " " << p.getheight() << endl;
	return 0;
}

2.7 类对象作为类成员

C++类中的成员可以是另一个类的对象,称该成员为对象成员。

#include<iostream>
#include<string>
using namespace std;
class Phone{
public:
	Phone(string name) {
		this->name = name;
		cout << "phone" << endl;
	}
	string name;
	~Phone() {
		cout << "~phone" << endl;
	}
};
class person{
public:
	person(string myname, string pname) : name(myname), myphone(pname) { 
		cout << "person" << endl; 
	}
	~person() {
		cout << "~person" << endl;
	}
	string name;
	Phone myphone;
};
int main() {
	//当其他对象作为本类成员,构造先构造类对象,在构造自身,析构相反
	person p("zhinen","huawei");
	cout << p.name << " " << p.myphone.name << endl;
	return 0;
}

2.8 静态成员

静态成员就是在成员变量和成员函数前加上关键词static。静态成员分为:
1)静态成员变量:所i有对象共享同一份数据;在编译阶段分配内存;类内声明,类外初始化。
2)静态成员函数:所有对象共享同一个函数;静态成员函数只能访问静态成员变量。

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	static void fun() {
		//静态成员函数只能访问静态成员变量。无法区分到底是哪个对象调用的。
		//grade = 100;//错误
		id = 1;//
		cout << "static function" << endl;
	}
	//类内声明,类外初始化。
	static int id;
	int grade;
private:
	//静态成员函数也是有访问权限的,私有类外访问不到
	static void func() {
		cout << "func" << endl;
	}
};
//类内声明,类外初始化。
int person::id = 0;
int main() {
	//通过对象访问
	person p;
	p.fun();
	//通过类名访问
	person::fun();
	return 0;
}

3. C++对象模型和this指针

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

在C++中,类内的成员变量和成员函数分开存储,只有非静态成员变量菜属于类的对象上。

#include<iostream>
#include<string>
using namespace std;
class person{
public:
private:
};
class animal {
public:
	int a;
};
class cat {
public:
	int a;
	static int b;
};
class dog {
public:
	int a;
	void fun() {}
};
class tiger {
public:
	int a;
	static void fun() {}
};
int main() {
	person p;
	//空对象占用的内存空间为1.
	//C++比那一起会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
	//每个空对象也应该有一个独一无二的内存地址
	cout << sizeof(p) << endl;
	//非静态成员,属于类的对象上
	animal a1;
	cout << sizeof(a1) << endl;
	//静态成员不属于类对象上
	cat c1;
	cout << sizeof(c1) << endl;
	//非静态成员函数,不属于类对象上
	dog d1;
	cout << sizeof(d1) << endl;
	//静态成员函数,不属于类的对象上
	tiger t1;
	cout << sizeof(t1) << endl;
	return 0;
}

3.2 this指针的概念

this指针指向被调用的成员函数所属的对象,this指针是隐含每一个非静态成员函数内的一种指针。this指针不需要定义,直接使用即可。this指针的用途:
1)当形参和成员变量同名时,可用this指针来区分
2)在类的非静态成员函数中返回本身对象,可使用return *this;

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	//解决名称冲突
	person(int age) {
		//this指针指向的是 被调用的成员函数 所属的对象
		this->age = age;
	}
	//
	person& add(person &p) {
		this->age += p.age;
		return *this;
	}
	int age;
private:
};
int main() {
	person p1(18);
	cout << p1.age << endl;
	person p2(20);
	//链式编程思想
	p2.add(p1).add(p1);
	cout << p2.age << endl;
	return 0;
}

3.3 空指针访问成员函数

C++中空指针也可以调用成员函数的,但是也要注意有没有用到this指针。如果用到,需要加以判断保证代码的健壮性。

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	person(string name = "zhinen", int age = 18) {
		this->name = name;
		this->age = age;
	}
	void getname() {
		cout << name << endl;//this->name
	}
	void getage() {
		cout << age << endl;//this->age
	}
	void fun() {
		cout << "person" << endl;
	}
private:
	string name;
	int age;
};
class cat {
public:
	cat(string name = "zhinen", int age = 18) {
		this->name = name;
		this->age = age;
	}
	void getname() {
		if (this == NULL)
			return;
		cout << name << endl;//this->name
	}
	void getage() {
		if (this == NULL)
			return;
		cout << age << endl;//this->age
	}
	void fun() {
		cout << "person" << endl;
	}
private:
	string name;
	int age;
};
int main() {
	person* p=NULL;
	p->fun();//可
	//错误,引发了异常: 读取访问权限冲突。this 是 nullptr。
	/*p->getage();
	p->getname();*/
	cat* c = NULL;
	//可
	c->getage();
	c->getname();
	return 0;
}

3.4 const修饰成员函数

常函数:
1)成员函数后加const称之为常函数;
2)常函数内不可以修改成员属性
3)成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
1)声明对象前加const称该对象为常对象
2)常对象只能调用常函数

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	person(string name = "zhinen", int age = 18) {
		this->name = name;
		this->age = age;
	}
	//this指针的本质是指针常量,指针的指向是不可修改的
	void getname() const{//相当于const person *const this;让指针指向的值也不可以改变
		//this->name = "zjw";//报错
		mininame = "awen";//关键字mutable可修改
		cout << name << " " << mininame << endl;//this->name
	}
	void getage() {
		cout << age << endl;//this->age
	}
	void fun() {
		cout << "person" << endl;
	}
	string name;
	int age;
	mutable string mininame;
};
int main() {
	const person p;//常对象
	//p.name = "123";//错误
	p.mininame = "zhi";//mininame是特殊值
	//p.getage();//错误,常对象只能调用常函数
	p.getname();
	return 0;
}

4.友元

友元的目的就是让一个函数或者类访问另一个类中私有成员。友元的关键字为friend。友元函数的三种实现:

4.1全局函数做友元

#include<iostream>
#include<string>
using namespace std;
class person{
	friend void fun(person& p);//好朋友可以看手机
public:
	person() {
		name = "zhinen";
		phone = "private";
	}
	string name;
private:
	string phone;
};
//全局函数
void fun(person& p) {
	cout << p.name << endl;
	cout << p.phone << endl;
}
int main() {
	person p;
	fun(p);
	return 0;
}

4.2 类做友元

#include<iostream>
#include<string>
using namespace std;
class person{
	friend class person1;
public:
	person() {
		name = "zhinen";
		phone = "private";
	}
	string name;
private:
	string phone;
};
class person1 {
public:
	void visit();
	person* p;
	person1() {
		p = new person;
	}
};
void person1::visit() {
	cout << p->name << endl;
	cout << p->phone << endl;
}
int main() {
	person1 p;
	p.visit();
	return 0;
}

4.3 成员函数做友元

#include<iostream>
#include<string>
using namespace std;
class person;
class person1 {
public:
	void visit();
	person * p;
	person1();
};
class person{
	friend void person1::visit();
public:
	person() {
		name = "zhinen";
		phone = "private";
	}
	string name;
private:
	string phone;
};
void person1::visit() {
	cout << p->name << endl;
	cout << p->phone << endl;
}
person1::person1() {
	p = new person;
}
int main() {
	person1 p;
	p.visit();
	return 0;
}

5.运算符重载

运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。

5.1 加号运算符重载

定义两个自定义数据类型相加的运算。
全局函数实现运算符重载:
本质:person p3=operator+(p1,p2);

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	int a;
	int b;
};
person operator+(person& p1, person& p2) {
	person temp;
	temp.a = p1.a + p2.a;
	temp.b = p1.b + p2.b;
	return temp;
}
int main() {
	person p1;
	p1.a = 10;
	p1.b = 10;
	person p2;
	p2.a = 10;
	p2.b = 10;
	person p3 = p1 + p2;
	cout << p3.a << " " << p3.b << endl;
	return 0;
}

或者,成员函数实现函数重载:
本质person p3=p1.operator+(p2);

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	person operator+(person& p) {
		person temp;
		temp.a = this->a + p.a;
		temp.b = this->b + p.b;
		return temp;
	}
	int a;
	int b;
};
int main() {
	person p1;
	p1.a = 10;
	p1.b = 10;
	person p2;
	p2.a = 10;
	p2.b = 10;
	person p3 = p1 + p2;
	cout << p3.a << " " << p3.b << endl;
	return 0;
}

总结:对于内置的数据类型的表达式的运算符是不可能改变的;不要滥用运算符重载。

5.2 左移运算符重载

作用:可以输出自定义数据类型。

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	//不会利用成员函数重载<<运算符,因为无法实现cout在左侧
	//void operator<<(cout) {}//p<<cout;

	int a;
	int b;
};
void operator<<(ostream &cout, person& p) {
	cout << p.a << " " << p.b << endl;
}
int main() {
	person p1;
	p1.a = 10;
	p1.b = 10;
	cout << p1;
	return 0;
}

链式思想实现:

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	//不会利用成员函数重载<<运算符,因为无法实现cout在左侧
	//void operator<<(cout) {}//p<<cout;

	int a;
	int b;
};
ostream &operator<<(ostream &cout, person& p) {
	cout << p.a << " " << p.b;
	return cout;
}
int main() {
	person p1;
	p1.a = 10;
	p1.b = 10;
	cout << p1 << endl;
	return 0;
}

5.3 递增运算符重载

作用:通过重载递增运算符,实现自己的整型数据。

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	//前置++运算符
	//返回引用是为了对一个数一直进行++
	person& operator++() {
		a++;
		return *this;
	}
	//后置++运算符
	//int代表占位参数,可以用于区分前置和后置递增
	person operator++(int) {
		person p = *this;
		a++;
		return p;
	}
	int a;
};
ostream &operator<<(ostream &cout, person& p) {
	cout << ++p.a << endl;
	cout << p.a++ << endl;
	cout << p.a << endl;
	return cout;
}

int main() {
	person p1;
	p1.a = 10;
	cout << p1 << endl;
	return 0;
}

5.4 递减运算符重载

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	//前置--运算符
	//返回引用是为了对一个数一直进行--
	person& operator--() {
		a--;
		return *this;
	}
	//后置--运算符
	//int代表占位参数,可以用于区分前置和后置递减
	person operator--(int) {
		person p = *this;
		a--;
		return p;
	}
	int a;
};
ostream &operator<<(ostream &cout, person& p) {
	cout << --p.a << endl;
	cout << p.a-- << endl;
	cout << p.a << endl;
	return cout;
}

int main() {
	person p1;
	p1.a = 10;
	cout << p1 << endl;
	return 0;
}

5.5 赋值运算符重载

C++编译器至少给一个类添加4个函数:
1)默认构造函数:无参,函数体为空
2)默认析构函数:无参,函数体为空
3)默认拷贝函数,对属性进行值拷
4)赋值运算符operator=,对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题。

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	person(int a){
		this->a = new int(a);
	}
	person& operator=(person &p) {
		//应该判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
		if (a != NULL) {
			delete a;
			a = NULL;
		}
		a = new int(*p.a);
		return *this;
	}
	~person() {
		if (a != NULL) {
			delete a;
			a = NULL;
		}
	}
	int *a;
};
int main() {
	//堆区内存重复释放,程序崩溃,使用编译器提供的拷贝函数
	/*person p1(20);
	person p2(22);
	p2 = p1;
	cout << *p1.a << endl;
	cout << *p2.a << endl;*/
	person p1(20);
	person p2(22);
	p2 = p1;
	cout << *p1.a << endl;
	cout << *p2.a << endl;
	return 0;
}

5.6 关系运算符重载

作用:重载关系运算符,可以让两个自定义类型的对象进行对比操作。

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	person(int a,string b){
		this->a = a;
		this->b = b;
	}
	bool operator==(person& p) {
		if (this->a == p.a && this->b == p.b)
			return true;
		return false;
	}
	bool operator!= (person & p) {
		if (this->a == p.a && this->b == p.b)
			return false;
		return true;
	}
	int a;
	string b;
};
//也可以用这种方法,全局函数
//bool operator==(person& p1, person& p2) {
//	if (p1.a == p2.a && p1.b == p2.b)
//		return true;
//	return false;
//}
int main() {
	person p1(18, "zhinen");
	person p2(18, "zhinen");
	if (p1 == p2) {
		cout << "==" << endl;
	}
	else {
		cout << "!=" << endl;
	}
	if (p1 != p2) {
		cout << "!=" << endl;
	}
	else {
		cout << "==" << endl;
	}

	return 0;
}

5.7 函数调用运算符重载

函数调用运算符()也可以重载,由于重载后使用的方式非常像函数的调用,因此称为仿函数。仿函数没有固定的写法,非常灵活。

#include<iostream>
#include<string>
using namespace std;
class person{
public:
	void operator()(string str) {
		cout << str << endl;
	}
};
class person1 {
public:
	int operator()(int a,int b) {
		return a + b;
	}
};
int main() {
	person p1;
	p1("zhinen");
	person1 p2;
	cout << p2(10, 20) << endl;
	return 0;
}

6. 继承

继承是面向对象的三大特性之一。有些类与类之间存在特殊的关系,继承的好处:减少重复的代码。

6.1 继承的基本语法

语法:class 子类 : 继承方式 父类
子类也成为派生类;父类也成为基类。
派生类中的成员,包含两大部分:一类是从基类继承过来的,一类是自己增加的成员,从基类继承过来的表现其共性,而新增的成员体现了其个性。

6.2 继承方式

继承的方式一共有三种:
1)公共继承
2)保护继承
3)私有继承
在这里插入图片描述

#include<iostream>
#include<string>
using namespace std;
class A {
public:
	int a;
protected:
	int b;
private:
	int c;
};
class B:public A {
public:
	void fun() {
		a = 10;//父类中的公共权限成员,到子类依然是公共权限。
		b = 10;//父类中的保护权限成员,到子类依然是保护权限。
		//c = 20;//父类中的私有权限成员,子类访问不到。
	}
};
class C :protected A {
public:
	void fun() {
		a = 10;//父类中的公共权限成员,到子类变为是保护权限。
		b = 10;//父类中的保护权限成员,到子类依然是保护权限。
		//c = 20;//父类中的私有权限成员,子类访问不到。
	}
};
class D :private A {
public:
	void fun() {
		a = 10;//父类中的公共权限成员,到子类变为是私有权限。
		b = 10;//父类中的保护权限成员,到子类变为是私有权限。
		//c = 20;//父类中的私有权限成员,子类访问不到。
	}
};
class F :public D {
public:
	void fun() {
		//a = 100;//父类中的私有权限成员,子类访问不到。
		//b = 100;//父类中的私有权限成员,子类访问不到。
		//c = 100;//父类中的私有权限成员,子类访问不到。
	}
};
int main() {
	B p;
	p.a = 100;
	//p.b = 100;//到子类中是保护权限,访问不到
	C p2;
	//p2.a = 100;//到子类中是保护权限,访问不到
	//p2.b = 100;//到子类中是保护权限,访问不到
	D p3;
	//p3.a = 100;//到子类中是私有权限,访问不到
	//p3.b = 100;//到子类中是私有权限,访问不到
	return 0;
}

6.3 继承中的对象模型

#include<iostream>
#include<string>
using namespace std;
class A {
public:
	int a;
protected:
	int b;
private:
	int c;
};
class B:public A {
public:
	int d;
};
int main() {
	//在父类中所有的非静态成员都会被子类继承下去,父类中私有成员属性,被编译器隐藏了,访问不到。
	cout << sizeof(B) << endl;
	return 0;
}

6.4 继承中构造和析构顺序

子类继承父类后,当子类创建子类对象,也会调用父类的构造函数。

#include<iostream>
#include<string>
using namespace std;
class A {
public:
	A() {
		cout << "A的构造函数" << endl;
	}
	~A() {
		cout << "A的析构函数" << endl;
	}
};
class B:public A {
public:
	B() {
		cout << "B的构造函数" << endl;
	}
	~B() {
		cout << "B的析构函数" << endl;
	}
};
class C :public B {
public:
	C() {
		cout << "C的构造函数" << endl;
	}
	~C() {
		cout << "C的析构函数" << endl;
	}
};
int main() {
	//继承中的构造函数:先构造父类再构造子类
	//析构函数:先析构子类,再析构父类(恰好相反)
	C p;
	return 0;
}

6.5 继承同名成员处理方式

当子类和父类出现同名的成员:
1)访问子类同名成员,直接访问即可
2)访问父类同名成员,需要加作用域

#include<iostream>
#include<string>
using namespace std;
class A {
public:
	A() {
		a = 100;
	}
	void fun() {
		cout << "A" << endl;
	}
	int a;
};
class B:public A {
public:
	B() {
		a = 200;
	}
	void fun() {
		cout << "B" << endl;
	}
	int a;
};
int main() {
	B p;
	cout << p.a << endl << p.A::a << endl;
	p.fun();//直接调用的是子类
	p.A::fun();
	//如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数
	//如果像访问,必须要加作用域
	return 0;
}

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

静态成员和非静态成员出现同名,处理方式一致:
1)访问子类同名成员,直接访问即可
2)访问父类同名成员,需要加作用域

#include<iostream>
#include<string>
using namespace std;
class A {
public:
	static void fun() {
		cout << "A" << endl;
	}
	static int a;
};
int A::a = 100;
class B:public A {
public:
	static void fun() {
		cout << "B" << endl;
	}
	static int a;
};
int B::a = 200;
int main() {
	//对象方式访问
	B p;
	cout << p.a << endl << p.A::a << endl;
	//类访问B::A::a:第一个::表示类名,第二个::访问父类作用域下
	cout << B::a << endl << B::A::a << endl;
	//对象访问
	p.fun();//直接调用的是子类
	p.A::fun();
	//类名访问
	B::fun();
	B::A::fun();
	//如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数
	//如果想访问,必须要加作用域
	return 0;
}

6.7 多继承语法

C++允许一个类继承多个类,但实际开发中不建议用多继承。
语法:class 子类 : 继承方式 父类1 , 继承方式 父类2,…
多继承可能会引发父类中有同名成员出现,需要加作用域区分。

#include<iostream>
#include<string>
using namespace std;
class A {
public:
	A() {
		a = 100;
	}
	int a;
};
class B {
public:
	B() {
		a = 200;
	}
	int a;
};
class C :public A,public B{
public:
	C() {
		c = 300;
		d = 400;
	}
	int c;
	int d;
};
int main() {
	cout << sizeof(C) << endl;
	C p;
	cout << p.A::a << endl << p.B::a << endl;
	return 0;
}

6.8 菱形继承

概念:两个派生类继承同一个基类,又有某个类同时继承这两个派生类,这种继承称为菱形继承,或者钻石继承。

#include<iostream>
#include<string>
using namespace std;
class A {
public:
	int a;
};
class B:public A {
public:
};
class C :public A{
public:
};
class D :public B, public C {

};
int main() {
	D p;
	//p.a = 200;//错
	//当菱形继承时,两个父类拥有相同的数据,需要加以作用域区分
	p.B::a = 100;
	p.C::a = 200;
	//a这份数据只要有一份就可以了,菱形继承导致数据有两份,资源浪费
	return 0;
}

利用虚继承解决菱形继承问题。

#include<iostream>
#include<string>
using namespace std;
class A {
public:
	int a;
};
//利用虚继承解决菱形继承问题(继承指针)
//继承之前加上关键字virtual,变为虚继承
//A类称为虚基类
class B:virtual public A {
public:
};
class C :virtual public A{
public:
};
class D :public B, public C {

};
int main() {
	C p;
	p.a = 12;
	cout << p.a << endl;
	return 0;
}

7.多态

7.1 多态的基本概念

多态时C++面向对象三大特性之一。多态分为两类:
1)静态多态:函数重载和运算符重载属于静态多态,复用函数名
2)动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态的区别:
1)静态多态的函数地址早绑定-编译阶段确定函数的地址
2)动态多态的函数地址晚绑定-运行阶段确定函数的地址

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-29 11:55:13  更:2022-04-29 11:56:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 23:36:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码