C++类与对象(友元类)

面向对象编程

  • 封装和数据隐藏
  • 继承
  • 抽象
  • 多态
  • 代码重用

对面向对象编程来说,一切都是对象,对象用类来描述。
类把对象的数据和操作数据的方法作为一个整体考虑。
定义类的语法:

class 类名{
	public:
		成员一数据类型 成员名一;
		成员二数据类型 成员名二;
		成员三数据类型 成员名三;
		........
		成员n数据类型 成员名n;
}

注意:

  • 类的成员可以是变量,也可以是函数。
  • 类的成员变量也叫属性。
  • 类的成员函数也叫方法/行为,类的成员函数可以定义在类的外面。
  • 用类定义一个类的变量叫做创建(或实例化)一个对象。
  • 类的成员变量和成员函数的作用域和生命周期与对象的作用域和生命周期相同。

例如

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string name;
	int age;
	void setvalue(string name1, int age1) {
		name = name1;
		age = age1;
	}
	void show() {
		cout << "姓名:" << name << ",年龄:" << age << endl;
	}
};
void CGirl::setvalue(string name1, int age1) {
	name = name1;
	age = age1;
}
int main() {

}

类的访问权限

类的成员有三种访问权限: public、private和protected,分别表示公有的、私有的和受保护的。
在类的内部(类的成员函数中),无论成员被声明为 public还是private,都是可以访问。
在类的外部(定义类的代码之外), ** 只能访问public成员,不能访问private、protected成员 **。
在一个类体的定义中,privatepublic可以出现多次。
结构体的成员缺省为public,类的成员缺省为private
private的意义在于隐藏类的数据和实现,把需要向外暴露的成员声明为public

简单使用类

编程思想和方法的改变,披着C++外衣的C程序员。

  • 类的成员函数可以直接访问该类其它的成员函数。
  • 类的成员函数可以重载。
  • 类指针的用法与结构体指针用法相同。
  • 类的成员可以是任意数据类型(类中枚举)。
  • 可以为类的成员指定缺省值(C++11标准)。
  • 类可以创建对象数组,就像结构体数组一样。
  • 对象可以作为实参传递给函数,一般传引用。
  • 可以用new动态创建对象,用delete释放对象。
  • 在类的外部,一般不直接访问(读和写)对象的成员,而是用成员函数。
  • 对象一般不用memset()清空成员变量,可以写一个专用于清空成员变量的成员函数。
  • 对类和对象用sizeof运算意义不大,一般不用。
  • 用结构体描述纯粹的数据,用类描述对象。
  • 类的分文件编写。
#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string name;
	int age=20;
	int sex;
	enum {girl=1,boy=2};
	void setvalue(string name1, int age1) {
		name = name1;
		age = age1;
	}
	void initdata() {//清空全部成员变量
		name.clear();
		age = 0;
	}
	void show() {
		cout << "姓名:" << name << ",年龄:" << age <<",性别"<<sex << endl;
	}
};
//void CGirl::setvalue(string name1, int age1) {
//	name = name1;
//	age = age1;
//}
int main() {
	CGirl girl;
	girl.setvalue("西施", 27);
	girl.show();
	girl.sex = girl.boy;
	girl.show();
}

构造函数和析构函数

构造函数: 在创建对象时,自动的进行初始化工作。
析构函数: 在销毁对象前,自动的完成清理工作。

构造函数

语法:类名(){....}

  • 访问权限必须是public
  • 函数名必须与类名相同
  • 没有返回值,也不用写void
  • 可以有参数,可以重载,也可以有默认参数
  • 创建对象时自动调用一次,不用手动调用
#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	int m_age;
	int m_memo[100];
	CGirl() {
		m_name.clear();
		m_age = 0;
		memset(m_memo, 0, sizeof(m_memo));
		cout << "调用了CGirl()构造函数" << endl;
	}
	CGirl(string name,int age) {
		m_name=name;
		m_age = age;
		memset(m_memo, 0, sizeof(m_memo));
		cout << "调用了CGirl(string name,int age)构造函数" << endl;
	}
	void show() {
		cout << "姓名:" << m_name << ",姓名:" << m_age << ",备注:" << m_memo << endl;
	}
};
int main() {
	CGirl girl("西施",8);
	girl.show();
	return 0;
}

析构函数

语法:~类名(){.....}

  • 访问权限必须时public
  • 函数名必须在类名前加~
  • 没有返回值,也不用写void
  • 没有参数,不能重载。
  • 销毁对象前只能自动调用一次,但是可以手动调用。
#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	int m_age;
	int m_memo[100];
	CGirl() {
		m_name.clear();
		m_age = 0;
		memset(m_memo, 0, sizeof(m_memo));
		cout << "调用了CGirl()构造函数" << endl;
	}
	CGirl(string name,int age) {
		m_name=name;
		m_age = age;
		memset(m_memo, 0, sizeof(m_memo));
		cout << "调用了CGirl(string name,int age)构造函数" << endl;
	}
	~CGirl() {//析构函数
		cout << "调用了析构函数" << endl;
	}
	void show() {
		cout << "姓名:" << m_name << ",姓名:" << m_age << ",备注:" << m_memo << endl;
	}
};
int main() {
	CGirl girl("西施",8);
	girl.show();
	return 0;
}

构造函数和析构函数的细节

  1. 如果没有提供构造/析构函数,编译器将提供空实现的构造/析构函数。
  2. 如果提供了构造/析构函数,编译器将不提供空实现的构造/析构函数。
  3. 创建对象的时候,如果重载了构造函数,编译器根据实参匹配相应的构造函数。
  4. 创建对象的时候不要在对象名后面加空的圆括号,编译器误认为是声明函数。(没有构造函数、构造函数没有参数、构造函数的参数都有默认参数)
  5. 在构造函数名后面加括号和参数不是调用构造函数,是创建匿名对象。
  6. 接受一个参数的构造函数允许使用赋值语法将对象初始化为一个值(可能会导致问题,不推荐)。
    CGirl=10
  7. 一下两行代码有本质区别:

CGirl girl =CGirl(”西施”,20);//显示创建对象
CGirl girl;//创建对象
girl =CGirl(”西施”,20);//创建匿名对象,然后给现有的对象赋值

  1. new/delete 创建/销毁对象时,也会调用构造/析构函数。
  2. 不建议在构造/析构函数中写太多的代码,可以调用成员函数。
  3. 除了初始化,不建议让构造做太多工作(只能成功不会失败)。-

拷贝构造函数

用一个已存在的对象创建新的对象,不会调用(普通)构造函数,而是调用拷贝构造函数
如果类中没有定义拷贝构造函数,编译器将提供一个拷贝构造函数,它的功能是把已存在对象的成员变量赋值给新对象的成员变量。
用一个已存在的对象创建新的对象语法:

类名 新对象名(已存在的对象名);
类名 新对象名=已存在的对象名;

拷贝构造函数的语法:

类名(const 类名& 对象名){}
默认拷贝构造函数

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	int m_age;
	CGirl() {
		m_name.clear();
		m_age = 0;
		cout << "调用了CGirl()构造函数" << endl;
	}
	~CGirl() {//析构函数
		cout << "调用了析构函数" << endl;
	}
	void show() {
		cout << "姓名:" << m_name << ",姓名:" << m_age  << endl;
	}
};
int main() {
	CGirl g1;
	g1.m_name = "西施";
	g1.m_age = 23;
	CGirl g2(g1);
	//CGirl g2=g1;
	g2.show();
	
	return 0;
}

我们自己定义拷贝构造函数

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	int m_age;
	CGirl() {
		m_name.clear();
		m_age = 0;
		cout << "调用了CGirl()构造函数" << endl;
	}
	CGirl(const CGirl& gg) {
		m_name = "漂亮的" + gg.m_name;
		m_age = gg.m_age - 1;
		cout << "调用了CGirl(const CGirl& gg)拷贝构造函数";
	}
	~CGirl() {//析构函数
		cout << "调用了析构函数" << endl;
	}
	void show() {
		cout << "姓名:" << m_name << ",姓名:" << m_age  << endl;
	}
};
int main() {
	CGirl g1;
	g1.m_name = "西施";
	g1.m_age = 23;
	CGirl g2(g1);
	//CGirl g2=g1;
	g2.show();
	
	return 0;
}

注意:

  • 访问权限必须是public。
  • 函数名必须与类名相同。
  • 没有返回值,不写void。
  • 如果类中定义了拷贝构造函数,编译器将不提供默认的考贝构造函数。
  • 以值传递的方式调用函数时,如果实参为对象,会调用拷贝构造函数。
  • 函数以值的方式返回对象时,可能会调用拷贝构造函数(VS会调用,Linux不会,g++编译器做了优化)。
  • 拷贝构造函数可以重载,可以有默认参数。
    类名(…...,const 类名& 对象名,...…){.…….}'
  • 如果类中重载了拷贝构造函数却没有定义默认的拷贝构造函数,编译器也会提供默认的拷贝构造函数。

浅拷贝和深拷贝

在这里插入图片描述
对象A和对象B同时指向同一块内存,如果A对象改了值,B多指的也得改,其中一个对象释放了内存,另一个对象的指针就成了野指针

在这里插入图片描述
如果A指向一块内存,那么就分配一快大小相同的内存,让指针B指向新内存。然后。再把指针A指向的内存中的数据拷贝到新内存中。这种拷贝的方法很彻底。在拷贝之后,大家各自操作自己的指针和内存。不会有任何冲突,浅拷贝的那些问题就不存在了。

初始化列表

构造函数的执行可以分成两个阶段:初始化阶段和计算阶段。初始化阶段先于计算阶段。

  • 初始化阶段:全部的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中。
  • 计算阶段:一般是指用于执行构造函数体内的赋值操作。

构造函数除了参数列表和函数体之外,还可以有初始化列表。
初始化列表的语法:
类名(形参列表):成员一(值一),成员二(值二)...,成员n(值n){..….}

CGirl() :m_name("西施"), m_age(23)
{
	cout << "调用了CGirl()构造函数" << endl;
}

例如:

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	int m_age;
	CGirl() :m_name("西施"), m_age(23)
	{
		cout << "调用了CGirl()构造函数" << endl;
	}
	~CGirl() {//析构函数
		cout << "调用了析构函数" << endl;
	}
	void show() {
		cout << "姓名:" << m_name << ",姓名:" << m_age  << endl;
	}
};
int main() {
	CGirl g1;
	g1.show();
	
	return 0;
}

或者

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	int m_age;
	CGirl() :m_name("西施"), m_age(23)
	{
		cout << "调用了CGirl()构造函数" << endl;
	}
	CGirl(string name,int age) :m_name("漂亮的"+name), m_age(age)
	{
		cout << "调用了CGirl()构造函数" << endl;
	}

	~CGirl() {//析构函数
		cout << "调用了析构函数" << endl;
	}
	void show() {
		cout << "姓名:" << m_name << ",姓名:" << m_age  << endl;
	}
};
int main() {
	CGirl g1("冰冰",18);
	g1.show();
	
	return 0;
}

注意:

  1. 如果成员已经在初始化列表中,则不应该在构造函数中再次赋值。
  2. 初始化列表的括号中可以是具体的值,也可以是构造函数的形参名,还可以是表达式。
  3. 初始化列表与赋值有本质的区别,如果成员是类,使用初始化列表调用的是拷贝构造函数而赋值则是先创建对象(调用普通构造函数),然后再赋值。
  4. 如果成员是类,初始化列表对性能略有提升。
  5. 如果成员是常量和引用,必须使用初始列表,因为常量和引用只能在定义的时候初始化。
  6. 如果成员是没有默认构造函数的类,则必须使用初始化列表。
  7. 拷贝构造函数也可以有初始化列表,但极少使用。
  8. 类的成员变量可以不出现在初始化列表中。

注意:在函数名后加const指的是函数不能修改类的成员变量

const 修饰成员函数

看这个代码会报错

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	int m_age;
	CGirl(const string &name,int age)
	{
		m_name = name;
		m_age = age;
		cout << "调用了CGirl(const string &name,int age)构造函数" << endl;
	}
	~CGirl() {//析构函数
		cout << "调用了析构函数" << endl;
	}
	void show() const{//在函数名后加const指的是函数不能修改类的成员变量
		m_name = 34;
		cout << "姓名:" << m_name << ",姓名:" << m_age  << endl;
	}
};
int main() {
	CGirl g1("冰冰",18);
	g1.show();
	
	return 0;
}

在类的成员函数后面加const关键字,表示在成员函数中保证不会修改调用对象的成员变量。

  1. mutable可以突破const的限制,被mutable修饰的成员变量,将永远处于可变的状态,在const修饰的函数中,mutable成员也可以被修改。
  2. 非const成员函数可以调用const成员函数和非const成员函数。
  3. const成员函数不能调用非const成员函数。
  4. 非const对象可以调用const修饰的成员函数和非const修饰的成员函数。
  5. const对象只能调用const修饰的成员函数,不能调用非cosnt修饰的成员函数。

疑问:

  1. 为什么要保护类的成员变量不被修改?
  2. 为什么用const保护了成员变量,还要再定义一个mutable关键字来突破const的封锁线?
  3. 到底有没有必要使用const和mutable这两个关键字?
    保护类的成员变量不在成员函数中被修改,是为了保证模型的逻辑正确,通过用const关键字来避免在函数中错误的修改了类对象的状态。并且在所有使用该成员函数的地方都可以更准确的预测到使用。该成员函数的带来的影响。而mutable则是为了能突破const的封锁线,让类的一些次要的或者是辅助性的成员变量随时可以被更改。没有使用const和mutable关键字当然没有错,const和mutable关键字只是给了建模工具更多的设计约束和设计灵活性,而且程序员也可以把更多的逻辑检查问题交给编译器和建模工具去做,从而减轻程序员的负担。

this指针

如果类的成员函数中涉及多个对象,在这种情况下需要使用this指针。
this指针存放了对象的地址,被作为隐藏参数传递给了成员函数,指向调用成员函数的对象(调用者对象)。
每个成员函数(包括构造函数和析构函数)都有一个this指针,可以用它访问调用者对象的成员。(可以解决成员变量名与函数形参名相同的问题) 。
*this可以表示整个对象。
例如我们实现一个这个:比较两个人的年龄,然而由更年轻的做自我介绍

如果在成员函数的括号后面使用const,那么将不能通过this指针修改成员变量。

首先我们可以这样写:

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	int m_age;
	CGirl(const string &name,int age)
	{
		m_name = name;
		m_age = age;
	}
	~CGirl() {//析构函数
	}
	void show() const{
		cout << "姓名:" << m_name << ",年龄:" << m_age  << endl;
	}
};
const CGirl& pk(const CGirl& gg1, const CGirl& gg2) {
	if (gg1.m_age < gg2.m_age) {
		return gg1;
	}
	return gg2;
}
int main() {
	//比较两个人的年龄,然而由更年轻的做自我介绍
	CGirl g1("冰冰",18);
	CGirl g2("西瓜", 20);
	const CGirl& g3 = pk(g1, g2);
	g3.show();
	return 0;
}

如果上面写的话就不是C++了,有点像C,下面才可以。

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	int m_age;
	CGirl(const string &name,int age)
	{
		m_name = name;
		m_age = age;
	}
	~CGirl() {//析构函数
	}
	void show() const{
		cout << "姓名:" << m_name << ",年龄:" << m_age  << endl;
	}
	const CGirl& pk(const CGirl& g) {
		if (g.m_age < this->m_age) return g;
		return *this;//*this指的是自己
	}
};
int main() {
	//比较两个人的年龄,然而由更年轻的做自我介绍
	CGirl g1("冰冰",18);
	CGirl g2("西瓜", 20);
	const CGirl& g3 = g2.pk(g1);
	g3.show();
	return 0;
}

还有这个

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	int m_age;
	CGirl(const string &name,int age)
	{
		m_name = name;
		m_age = age;
	}
	~CGirl() {//析构函数
	}
	void show() const{
		cout << "姓名:" << m_name << ",年龄:" << m_age  << endl;
	}
	const CGirl& pk(const CGirl& g) const{
		if (g.m_age < this->m_age) return g;
		return *this;//*this指的是自己
	}
};
int main() {
	//比较两个人的年龄,然而由更年轻的做自我介绍
	CGirl g1("冰冰",18);
	CGirl g2("西瓜", 20);
	CGirl g3("金莲", 21);
	CGirl g4("幂幂", 22);
	CGirl g5("西施", 23);
	const CGirl& g = g1.pk(g2).pk(g3).pk(g4).pk(g5);
	g.show();
	return 0;
}

类的静态成员

类的静态成员包括静态成员变量静态成员函数
用静态成员可以变量实现多个对象之间的数据共享,比全局变量更安全性。
static关键字把类的成员变量声明为静态,表示它在程序中(不仅是对象)是共享的。
静态成员变量不会在创建对象的时候初始化,必须在程序的全局区用代码清晰的初始化(用范围解析运算符)。
静态成员使用类名加范围解析运算符::就可以访问,不需要创建对象。
如果把类的成员声明为静态的,就可以它与类的对象独立开来(静态成员不属于对象)。
静态成员变量在程序中只有一份(生命周期与程序运行期相同,存放在静态存储区的),不论是否创建了类的对象,也不论创建了多少个类的对象。
静态成员函数只能访问静态成员,不能访问非静态成员。
静态成员函数中没有this指针。
非静态成员函数可以访问静态成员。
const静态成员变量可以在定义类的时候初始化。

对于这个int CGirl::m_age;//初始化类的静态成员变量这个是必须的。而且这个必须得放在全局中,不能放在全局。

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	static int m_age;
	CGirl(const string &name,int age)
	{
		m_name = name;
		m_age = age;
	}
	~CGirl() {//析构函数
	}
	void show() const{
		cout << "姓名:" << m_name << ",年龄:" << m_age  << endl;
	}
	void showname() {
		cout << "姓名:" << m_name << endl;
	}
	void showage() {
		cout << "年龄:" << m_age << endl;
	}
	const CGirl& pk(const CGirl& g) const{
		if (g.m_age < this->m_age) return g;
		return *this;//*this指的是自己
	}
};
int CGirl::m_age;//初始化类的静态成员变量。
int main() {
	CGirl g1("西施", 23);
	g1.showname();
	g1.showage();

	return 0;
}

这样也可以:

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	static int m_age;
	CGirl(const string &name,int age)
	{
		m_name = name;
		m_age = age;
	}
	~CGirl() {//析构函数
	}
	void show() const{
		cout << "姓名:" << m_name << ",年龄:" << m_age  << endl;
	}
	void showname() {
		cout << "姓名:" << m_name << endl;
	}
	void showage() {
		cout << "年龄:" << m_age << endl;
	}
	const CGirl& pk(const CGirl& g) const{
		if (g.m_age < this->m_age) return g;
		return *this;//*this指的是自己
	}
};
int CGirl::m_age=8;//初始化类的静态成员变量。
int main() {
	
	cout << "CGirl::m_age=" << CGirl::m_age << endl;
	CGirl g1("西施", 23);
	g1.showname();
	g1.showage();

	return 0;
}

如果把类也创建为静态的,那么就可以不创建对象而访问该成员。

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
public:
	string m_name;
	static int m_age;
	CGirl(const string &name,int age)
	{
		m_name = name;
		m_age = age;
	}
	~CGirl() {//析构函数
	}
	void show() const{
		cout << "姓名:" << m_name << ",年龄:" << m_age  << endl;
	}
	void showname() {
		cout << "姓名:" << m_name << endl;
	}
	static void showage() {
		cout << "年龄:" << m_age << endl;
	}
	const CGirl& pk(const CGirl& g) const{
		if (g.m_age < this->m_age) return g;
		return *this;//*this指的是自己
	}
};
int CGirl::m_age=8;//初始化类的静态成员变量。
int main() {
	CGirl::showage();
	cout << "CGirl::m_age=" << CGirl::m_age << endl;
	CGirl g1("西施", 23);
	g1.showname();
	g1.showage();

	return 0;
}

简单对象模型

在C语言中,数据和处理数据的操作(函数)是分开的。也就是说,C语言本身没有支持数据和函数之间的关联性。
C++用类描述抽象数据类型(abstract data type,ADT),在类中定义了数据和函数,把数据和函数关联起来。
对象中维护了多个指针表,表中放了成员与地址的对应关系。

class CGirl{
	public:
		char m_name[10];
		int m_age;
		CGirl(){memset(m_name,0,sizeof(m_name));m_age=0;}
		~CGirl(){}
		void showname(){cout<<"姓名:"<<m_name<<endl;}
		void showage(){cout<<"年龄:”<<m_age<<endl;}
}

在这里插入图片描述
C++类中有两种数据成员: nonstatic、static,三种函数成员: nonstatic、static、virtual

  • 对象的内存大小包括:
    • 1.所有非静态数据成员的大小;
    • 2.由内存对齐而填补的内存大小;
    • 3.为了支持virtual成员而产生的额外负担。
  • 静态成员变量属于类,不计算在对象大小之内。
  • 成员函数是分开存储的,不论对象是否存在都占用存储空间,在内存中只有一个副本,也不计算在对象大小之内。
  • 用空指针可以访问没有用到this指针的非静态成员函数。
  • 空对象的大小为1字节。在C++中空类会占一个字节,这是为了让对象的实例能够相互区别。
  • 在程序员看来C++是一个整体,但是对象的各种成员变量和成员函数实际上是分散在内存中的。类中的静态成员和全局变量是存放在一起的。
  • 用空指针可以调用没有用到this指针的非静态成员函数。
  • 对象的地址是第一个非静态成员变量的地址,如果类中没有非静态成员变量,编译器会隐含的增加一个1字节的占位成员。

友元

如果要访问类的私有成员变量,调用类的公有成员函数是唯一的办法,而类的私有成员函数则无法访问。
友元提供了另一访问类的私有成员的方案。友元有三种:

  • 友元全局函数。
  • 友元类。
  • 友元成员函数。
  1. 友元全局函数
    在友元全局函数中,可以访问另一个类的所有成员。
    对于这个例子:我们定义一个友元全局函数friend int main();这样在main函数中就可以访问了。
#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
	friend int main();//友元全局函数
public:
	string m_name;
	CGirl(){
		m_name = "西施";
		m_xw = 87;
	}
	~CGirl() {//析构函数
	}
	void showname() {
		cout << "姓名:" << m_name << endl;
	}
private:
	int m_xw;//胸围
	void showxw() {
		cout << "胸围:" << m_xw << endl;
	}
};
int main() {
	CGirl g;
	g.showname();
	g.showxw();
	return 0;
}

例如我们在定义一个这个friend void func();这样func中也可以访问了。

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {
	friend int main();//友元全局函数
	friend void func();
public:
	string m_name;
	CGirl(){
		m_name = "西施";
		m_xw = 87;
	
	}
	~CGirl() {//析构函数
	}
	void showname() {
		cout << "姓名:" << m_name << endl;
	}
private:
	int m_xw;//胸围
	void showxw() {
		cout << "胸围:" << m_xw << endl;
	}
};
void func() {
	CGirl g;
	g.showname();
	g.showxw();
}
int main() {
	func();
	return 0;
}
  1. 友元类
    在友元类所有成员函数中,可以访问另一个类的所有成员。
    友元类的注意事项:
  • 友元关系不能被继承。
  • 友元关系是单向的,不具备交换性。

若类B是类A的友元,类A不一定是类B的友元。B是类A的友元,类C是B的友元,类C不定是类A的友元,要看类中是否有相应的声明。

例如:定义一个超女类,里面有姓名还有胸围,在定义一个男朋友类,超女的胸围只能被他男朋友看到。friend class CBoy;其中CBoy是一个类

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl {//超女类
	friend class CBoy;//把男朋友设置成超女类的友元类
public:
	string m_name;
	CGirl(){
		m_name = "西施";
		m_xw = 87;
	
	}
	~CGirl() {//析构函数
	}
	void showname() {
		cout << "姓名:" << m_name << endl;
	}
private:
	int m_xw;//胸围
	void showxw() const{
		cout << "胸围:" << m_xw << endl;
	}
};
class CBoy {
public:
	void func(const CGirl& g) {
		cout << "我的女朋友的姓名是:" << g.m_name << endl;
		cout << "我的女朋友的胸围是" << g.m_xw << endl;
		g.showxw();
	}
	
};
int main() {
	CGirl g;
	CBoy b;
	b.func(g);
	return 0;
}
  1. 友元成员函数
    在友元类某成员函数中,可以访问另一个类的所有成员。
    如果要把男朋友类CBoy的某成员函数声明为超女类CGirl 的友元,声明和定义的顺序如下:
class CGirl;//前置声明
class CBoy{.....};
class CGirl{......};

然后就这样就可以了

#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
class CGirl;
class CBoy {
public:
	void func1(const CGirl& g);
	void func2(const CGirl& g);
};
class CGirl {//超女类
	friend void CBoy::func1(const CGirl& g);
	friend void CBoy::func2(const CGirl& g);
public:
	string m_name;
	CGirl(){
		m_name = "西施";
		m_xw = 87;
	
	}
	~CGirl() {//析构函数
	}
	void showname() {
		cout << "姓名:" << m_name << endl;
	}
private:
	int m_xw;//胸围
	void showxw() const{
		cout << "胸围:" << m_xw << endl;
	}
};
void CBoy::func1(const CGirl& g) {
	cout << "func1()我女朋友的胸围是:" << g.m_xw << endl;
}
void CBoy::func2(const CGirl& g) {
	cout << "func2()我女朋友的胸围是:" << g.m_xw << endl;
}
int main() {
	CGirl g;
	CBoy b;
	b.func1(g);
	b.func2(g);
	return 0;
}
  • 38
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值