类与对象(一)

访问权限

类在设计时共有三种权限:
image
相比JAVA的权限控制少了一个,更简洁明了了。
public 公共权限,类内类外都可访问。
protected 保护权限,类内可以访问,类外不可访问,但子类可访问父类中protected权限的成员。
private 私有权限,仅限类内访问。

class和struct的区别

image
名称不同,一个为自定义数据结构,一个为类,反正我以前都是把struct当成小型的自定义结构用,也没有往里面塞方法之类的。
唯一区别在于默认访问权限不同,class默认为私有,struct默认为公共的。

对象的初始化和清理

image
构造函数和析构函数,要求强制实现,要么自己定义,要么采用编译器自带的空实现。

构造函数的分类和调用

image
最常用的还是括号法。

构造函数的调用规则

image

浅拷贝和深拷贝

image
拷贝构造函数的分类,默认的拷贝构造函数为浅拷贝,可以理解为就是简单的值传递,这样会导致类中成员为指针变量,进行拷贝操作时会导致不同对象的指针指向同一个堆区空间,这样在析构函数释放时就会导致对于堆区空间的强制释放。代码如下:

#include<iostream>
#include<string>
using namespace std;

class Person
{
public:

	Person(int age, int height)
	{
		m_age = age;
		m_height = new int(height);
	}

	~Person()
	{
		if (m_height != NULL)
		{
			delete m_height;
			m_height = NULL;
		}
		cout << "调用了Person的析构函数" << endl;
	}

	int m_age;
	int* m_height;


};


int main(){
	
	Person p1 = Person(18,173);
	cout << p1.m_age << *p1.m_height << endl;

	Person p2 = Person(p1);
	cout << p2.m_age << *p2.m_height << endl;


	return 0;
}

解决办法就是自己写拷贝构造函数,在每次新生成对象时,再独自从堆区开辟一个空间分配即可。代码如下:

#include<iostream>
#include<string>
using namespace std;

class Person
{
public:

	Person(int age, int height)
	{
		m_age = age;
		m_height = new int(height);
	}

	Person(const Person& p)
	{
		if (p.m_height != NULL)
		{
			m_height = new int(*p.m_height);
		}
		m_age = p.m_age;
	}

	~Person()
	{
		if (m_height != NULL)
		{
			delete m_height;
			m_height = NULL;
		}
		cout << "调用了Person的析构函数" << endl;
	}

	int m_age;
	int* m_height;


};


int main(){
	
	Person p1 = Person(18,173);
	cout << p1.m_age << *p1.m_height << endl;

	Person p2 = Person(p1);
	cout << p2.m_age << *p2.m_height << endl;


	return 0;
}

初始化列表

在构造函数中,可以以下方式进行初始化成员变量。

类名(形参):成员变量(初值),成员变量(初值),...
{

}

例子如下:

class Person()
{
public:
	
	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;
}

类对象作为类成员时析构和构造的顺序

当一个类对象(a)作为另一个类(b)的成员时,先调用a的构造,后构造b的构造。析构时先调用b的析构,后调用a的析构。
栈的模式先进后出,b的构造时,需要利用a的信息,所以a应该先于b的构造,析构时,只有当a知道b要析构时,才会调用自己的析构,所以b的析构才会先于a的析构。

静态成员

image
静态成员记住一个特性,所有同一个类的对象共享,所以静态成员是依托于类而存在的,而不是依托于实例化的对象存在的。
所以静态成员变量实在编译阶段就分配内存了,在全局区。静态成员重要特性:类内声明,类外初始化!!!通过类名去访问静态成员变量或者静态成员函数时加上作用域::即可。
例子见下述博客:
https://blog.csdn.net/weixin_54907221/article/details/118494341

成员变量与成员函数存储

image
在类的存储上,只有非静态成员变量是和对象绑定的,静态成员变量存储在全局区中,函数不管静态和非静态也都是只存储一份,所有对象共享的调用,其中非静态成员函数根据this指针辨别是哪一个对象的调用。

this指针

image
this指针,之前学习JAVA的时候有所了解,但在类的非静态函数中返回对象本身,还是挺稀奇的。
这里有两个例子,关于返回对象本身。
链式编程

#include<iostream>
#include<string>
using namespace std;

class Person
{
public:

	Person(int a)
	{
		this->a = a;
	}

	Person& add(int a)
	{
		this->a += a;
		return *this;
	}
	int a;


};


int main(){
	
	Person p(10);
	p.add(10).add(10).add(10);
	cout << p.a << endl;

	return 0;
}

通过引用的方式,返回对象本身,这样就可以一直不断的接.add()了,同理观察cout<<的结构也是一样的,说明cout的设计也是同理。

这里就有人问了,为什么要用引用的形式返回,直接返回Person会怎么样,可见下述代码:

#include<iostream>
#include<string>
using namespace std;

class Person
{
public:

	Person(int a)
	{
		this->a = a;
	}

	Person add(int a)
	{
		this->a += a;
		return *this;
	}
	int a;


};


int main(){
	
	Person p(10);
	p.add(10).add(10).add(10);
	cout << p.a << endl;

	return 0;
}

代码运行结果为20,只被加了一次,原因是Person类型的返回是返回一个匿名对象,并没有被任何对象接受,这就导致了p.a只被接受一次。

const修饰成员函数和对象

image
当const修饰成员函数时,放在函数之后,不可以修改成员属性,成员属性中有关键字mutable时,才可以修改。
在实例化对象时,若加const修饰,则为常对象,常对象只能调用常函数和修改mutable的成员变量。

友元

image
友元是指允许函数或者类访问另一个类中的私有成员的关键字,friend。

全局函数做友元

全局函数做友元,需要将全局函数的声明放入类中,且在其上方加入friend。

#include<iostream>
#include<string>
using namespace std;

class Building
{
	//goodGay全局函数是Building的好朋友,可以访问Building的私有成员
	friend void goodGay(Building& building);

public:
	Building()
	{
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}

	string m_SittingRoom;

private:
	string m_BedRoom;
};

//全局函数
void goodGay(Building &building)
{
	cout << "好基友的全局函数 正在访问:" << building.m_SittingRoom << endl;

	cout << "好基友的全局函数 正在访问:" << building.m_BedRoom << endl;
}

void test01()
{
	Building building;
	goodGay(building);
}

int main(){
	
	test01();

	return 0;
}

类做友元

将友元类放入当前类中,前加friend。

#include<iostream>
#include<string>
using namespace std;

class Building
{
	//GoodGay类作为Building类的私有成员,可以访问其私有成员
	friend class GoodGay;
public:
	Building();
	
	string m_SittingRoom;

private:
	string m_BedRoom;
};

Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}

class GoodGay
{
public:
	GoodGay();
	void visit(); //参观函数,访问Building中的属性

	Building* building;

};

GoodGay::GoodGay()
{
	//创建一个建筑物的对象
	building = new Building;
}

void GoodGay::visit()
{
	cout << "好基友类正在访问 " << building->m_SittingRoom << endl;

	cout << "好基友类正在访问 " << building->m_BedRoom << endl;
}

void test01()
{
	GoodGay gg;
	gg.visit();
}

int main(){
	
	test01();

	return 0;
}

成员函数做友元

成员函数做友元,语法题先为frieng void 类名::成员函数名();
注意之间声明的顺序,A要做B的友元,A可以访问B的私有成员,则在B中写上述语句,且A得在B之前声明。

#include<iostream>
#include<string>
using namespace std;

class Building;

class GoodGay
{
public:
	GoodGay();
	void visit(); //参观函数,访问Building中的私有属性
	void visit2();//不可访问私有属性

	Building* building;

};


class Building
{
	friend void GoodGay::visit();
public:
	Building();

	string m_SittingRoom;

private:
	string m_BedRoom;
};




Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}



GoodGay::GoodGay()
{
	//创建一个建筑物的对象
	building = new Building;
}

void GoodGay::visit()
{
	cout << "好基友类正在访问 " << building->m_SittingRoom << endl;

	cout << "好基友类正在访问 " << building->m_BedRoom << endl;
}

void GoodGay::visit2()
{
	cout << "好基友类正在访问 " << building->m_SittingRoom << endl;

}

void test01()
{
	GoodGay gg;
	gg.visit();

	gg.visit2();
}

int main(){
	
	test01();

	return 0;
}

运算符重载

运算符重载和自定义运算符的本质都是定义了一个operator+运算符的函数。
加法重载:

#include<iostream>
#include<string>
using namespace std;

class Person
{
public:

	Person()
	{

	}

	Person operator+(const Person& p)
	{
		Person temp;
		temp.m_a = this->m_a + p.m_a;
		temp.m_b = this->m_b + p.m_b;
		return temp;
	}

	Person(int a,int b)
	{
		m_a = a;
		m_b = b;
	}

	int m_a;
	int m_b;
};

void test01()
{
	Person p1(10,20);
	Person p2(20, 10);
	Person p3 = p1 + p2;
	cout << p3.m_a << ' ' << p3.m_b << endl;
}

int main(){
	
	test01();

	return 0;
}

左移运算符

#include<iostream>
#include<string>
using namespace std;

class Person
{
public:

	//一般不会利用成员函数去重载左移运算符,因为这样会导致p在左边
	/*void operator<<(cout )
	{
		
	}*/
	Person()
	{

	}
	Person(int a,int b)
	{
		m_a = a;
		m_b = b;
	}

	int m_a;
	int m_b;
};

//只能利用全局函数重载左移
ostream& operator<<(ostream &cout,Person &p)
{
	cout << "m_a = " << p.m_a << ' ' << "m_b = " << p.m_b << endl;
	return cout;
}

void test01()
{
	Person p(10,20);
	cout << p << endl;
}

int main(){
	
	test01();

	system("pause");
	return 0;
}

递增运算符重载

#include<iostream>
#include<string>
using namespace std;

//重载递增运算符
class MyInteger
{
	friend ostream& operator<< (ostream& cout, MyInteger myint);
public:
	MyInteger()
	{
		m_Num = 0;
	}

	//重载前置++运算符,用返回引用方便链式编程
	MyInteger& operator++()
	{
		m_Num++;
		return *this;
	}

	//重载后置++运算符,用int占位参数,用以区分是前置和后置递增。
	//注意返回值,返回引用的话就会导致返回局部变量的地址了,非法操作。
	MyInteger operator++(int)
	{
		MyInteger temp = *this;
		this->m_Num++;
		return temp;
	}

private:
	int m_Num;
};

ostream&  operator<< (ostream& cout,MyInteger myint)
{
	cout << myint.m_Num << endl;
	return cout;
}

void test01()
{
	MyInteger myint;
	cout << ++myint << endl;
	cout << myint++ << endl;
	cout << myint << endl;
}  

int main(){
	
	test01();

	system("pause");
	return 0;
}

赋值运算符重载

image

#include<iostream>
#include<string>
using namespace std;

class Person
{
public:

	Person(int age)
	{
		m_Age =	new int(age);
	}

	~Person()
	{
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}
	}

	//重载赋值运算
	Person& operator=(Person &p)
	{
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}
		
		m_Age = new int(*p.m_Age);
		return *this;
	}

	int* m_Age;
};

void test01()
{
	Person p1(18);

	Person p2(20);

	Person p3(30);

	p3 = p2 = p1;

	cout << "p1的年龄为:" << *p1.m_Age << endl;

	cout << "p2的年龄为:" << *p2.m_Age << endl;

	cout << "p3的年龄为:" << *p3.m_Age << endl;
}  

int main(){
	
	test01();

	system("pause");
	return 0;
}

重载函数调用,仿函数

#include<iostream>
#include<string>
using namespace std;

class Person
{
public:
	//重载小括号
	void operator() (string st)
	{
		cout << st << endl;
	}
 };

void prints(string st)
{
	cout << st << endl;
}

void test01()
{
	Person ps;
	ps("Hello World");//因为和函数很类似,所以称为仿函数。
	prints("Hello World");
	Person()("Hello World"); // 使用匿名对象使用仿函数。
}  

int main(){
	
	test01();

	system("pause");
	return 0;
}
posted @ 2024-11-10 16:46  逆天峰  阅读(2)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//