C++友元+内存管理
Posted 山舟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++友元+内存管理相关的知识,希望对你有一定的参考价值。
文章目录
一、友元
友元分为友元函数和友元类。
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
1.友元函数
如果想要用cout直接输出日期而不是用成员函数来间接输出,就需要重载>>来实现。
注意cout的类型是ostream,调用的时候传入cout。
代码如下:
class Date
{
public:
void operator<<(ostream& out)//void operator<<(Date* this, ostream& out)
{
out << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
但是由于重载时第一个参数一定是隐含的this指针,所以在使用重载后的<<时需要如下使用。
代码如下:
int main()
{
Date d1;
d1 << cout;//也即d1.operator<<(cout);
return 0;
}
这显然不符合cout的使用习惯,同时代码的可读性很差,甚至不如一个间接的print函数。
于是有了一个问题:<<的重载写在类内会导致代码可读性下降,写在类外又无法访问类的成员变量。
那么怎样解决这个问题呢——用友元。
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
代码如下:
class Date
{
//定义友元函数(在类内)
//只需在函数声明前加上friend即可
friend void operator<<(ostream& out, const Date& d);
public:
Date(int year = 1970, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
void operator<<(ostream& out, const Date& d)
{
out << d._year << "-" << d._month << "-" << d._day << endl;
}
int main()
{
Date d1;
cout << d1;//即operator<<(cout, d1);
return 0;
}
但是由于operator<<返回值是空,所以无法实现连续输出,只需将operator<<的返回值改为ostream&,即继续返回out,这样每次输出后的返回值都是out,可支持连续输出。
代码如下:
class Date
{
friend ostream& operator<<(ostream& out, const Date& d);
public:
Date(int year = 1970, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "-" << d._month << "-" << d._day << endl;
return out;
}
int main()
{
Date d1;
Date d2(2021, 7, 21);
cout << d1 << d2;//可连续输出
return 0;
}
输入重载>>同理。
代码如下:
class Date
{
public:
friend istream& operator>>(istream& in, Date& d);
private:
int _year;
int _month;
int _day;
};
//cin类型为istream
//返回值为istream可支持连续输入多个Date对象
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
注意:
(1)友元函数可访问类的私有和保护成员,但不是类的成员函数。
(2)友元函数不能用const修饰。
(3)友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
(4)一个函数可以是多个类的友元函数。
(5)友元函数的调用与普通函数的调用和原理相同。
2.友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
如下面的代码中Date类中的成员函数想要访问Time类型的对象_t的成员变量,则需要将Date类定义为Time类的友元。
代码如下:
class Date
{
public:
Date(int year = 1970, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void SetTime()
{
_t._hour = 0;
_t._minute = 1;
_t._second = 1;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
class Time
{
friend class Date;
public:
Time(int hour = 0, int minute = 1, int second = 1)
{
_hour = hour;
_minute = minute;
_second = second;
}
private:
int _hour;
int _minute;
int _second;
};
注意:
(1)友元关系是单向的,不具有交换性。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time
类的私有成员变量,但不能在Time类中访问Date类中私有的成员变量。
(2)友元关系不能传递。
如果B是A的友元,C是B的友元,不能说明C是A的友元。
二、C语言动态内存管理的方式
关于C语言动态内存管理的相关知识请移步C语言动态内存管理。
三、 C++内存管理方式
通过new和delete操作符进行动态内存管理。
注意C语言中与动态内存相关的malloc,realloc,calloc和free是函数,而C++中的new和delete是操作符。
1.new/delete操作内置类型
new/delete和malloc/free针对内置类型没有任何差别,只是写法不一样。
代码如下:
void Test()
{
//注意new和delete的格式
//动态申请一个int类型的空间,不初始化
int* ptr1 = new int;
//动态申请一个double类型的空间并初始化为3.14
//注意可以直接初始化
double* ptr2 = new double(3.14);
//动态申请10个char类型的空间
char* ptr3 = new char[3];
delete ptr1;
delete ptr2;
delete[] ptr3;//不要忘记[]
}
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[]。 new和delete、new[]和delete[]一定要匹配,否则可能出问题。
2.new和delete操作自定义类型
代码如下:
class ListNode
{
public:
ListNode(int val = 0)
:_val(val)
, _prev(nullptr)
, _next(nullptr)
{
cout << "调用构造函数" << endl;
}
~ListNode()
{
cout << "调用析构函数" << endl;
}
private:
int _val;
ListNode* _prev;
ListNode* _next;
};
int main()
{
ListNode* c = (ListNode*)malloc(sizeof(ListNode));
free(c);
ListNode* cpp = new ListNode;
delete cpp;
return 0;
}
调试如下,可以观察到malloc仅仅开出所需的空间,并不会对指针指向地址的成员变量进行初始化;而new不仅开出所需的空间,还会会对指针指向地址的成员变量进行初始化。
调试结果如下:
运行如下,可以观察到delete会调用自定义类型的析构函数,而free不会。
运行如下:
在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。
四、 malloc/free和new/delete
1.共同点
从堆上申请空间,并且需要手动释放。
2.不同点
(1)malloc和free是函数,new和delete是操作符
(2)malloc申请的空间不会初始化,new可以初始化
(3)malloc申请空间时,需要手动计算空间大小,new只需在其后跟上空间的类型即可
(4)malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
(5)malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
(6)申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理
感谢阅读,如有错误请批评指正
以上是关于C++友元+内存管理的主要内容,如果未能解决你的问题,请参考以下文章