目录

C复试笔记三

C++复试笔记(三)

1.友元函数和友元类

1.1友元函数

友元函数的经典实例是重载 “«” 和 “»” , 去重载operator«

,然后发现没办法将

operator«

重载成成员函数。

因为

cout

输出流对象和隐含的

this

指针在抢占第一个参数的位置

this

指针默认是第一个参数也就是左操作 数了。但是实际使用中cout

需要是第一个形参对象,才能正常使用。所以要将

operator«

重载成 全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator»

同理。

友元函数

可以

直接访问

类的

私有

成员,它是

定义在类外部

普通函数

,不属于任何类,但需要在

类的内部声明,声明时需要加

friend

关键字。

class Date
{
     friend ostream& operator<<(ostream& _cout, const Date& d);
     friend istream& operator>>(istream& _cin, Date& d);
public:
     Date(int year = 1900, int month = 1, int day = 1)
         : _year(year)
         , _month(month)
         , _day(day)
         {}
private:
     int _year;
     int _month;
     int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
     _cout << d._year << "-" << d._month << "-" << d._day;
     return _cout; 
}
istream& operator>>(istream& _cin, Date& d)
{
     _cin >> d._year;
     _cin >> d._month;
     _cin >> d._day;
     return _cin;
}
int main()
{
     Date d;
     cin >> d;
     cout << d << endl;
     return 0;
}

说明:

  • 友元函数

    可访问类的私有和保护成员,但

    不是类的成员函数

  • 友元函数

    不能用

    const

    修饰

  • 友元函数

    可以在类定义的任何地方声明,

    不受类访问限定符限制

  • 一个函数可以是多个类的友元函数

  • 友元函数的调用与普通函数的调用原理相同

1.2友元类

  • 友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
  • 友元关系是单向的,不具有交换性。

比如上述

Time

类和

Date

类,在

Time

类中声明

Date

类为其友元类,那么可以在

Date

类中直接

访问

Time

类的私有成员变量,但想在

Time

类中访问

Date

类中私有的成员变量则不行。

  • 友元关系不能传递

如果

C

B

的友元,

B

A

的友元,则不能说明

C

A

的友元。

  • 友元关系不能继承。
class Time
{
   friend class Date;   // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类
中的私有成员变量
public:
 Time(int hour = 0, int minute = 0, int second = 0)
 : _hour(hour)
 , _minute(minute)
 , _second(second)
 {}
   
private:
   int _hour;
   int _minute;
   int _second;
};
class Date
{
public:
   Date(int year = 1900, int month = 1, int day = 1)
       : _year(year)
       , _month(month)
       , _day(day)
   {}
   
   void SetTimeOfDate(int hour, int minute, int second)
   {
       // 直接访问时间类私有的成员变量
       _t._hour = hour;
       _t._minute = minute;
       _t._second = second;
   }
   
private:
   int _year;
   int _month;
   int _day;
   Time _t;
};

2.解引用和后置++

int main()
{
	int a[10] = { 0,1,-2,3,-4,5,-6,7,-8,9 }, * p = a + 1;
	cout << *p++ << endl;
	return 0;
}

https://i-blog.csdnimg.cn/direct/10c55bd95ea840a1a22000596d329371.png

  1. 然后,定义了一个指向整型的指针 p 并将其初始化为 a + 1 。这里, a 是数组名,也可以作为指向数组第一个元素的指针使用。所以, a + 1 就是指向数组第二个元素的指针(即指向值为 1 的那个元素)。

  2. cout << *p++ << endl; 这行代码执行了以下操作:

    • *p :首先解引用指针 p ,得到它当前指向的值,这里是 1
    • p++ :然后,执行 p 的自增操作,使 p 指向下一个元素。但是需要注意的是,根据C++运算符优先级, p++ 在此上下文中是在 *p 获取值之后才进行的,这意味着这次自增不会影响本次解引用的结果。

因此,最终输出的是 1 ,这是 p 初始指向位置的值。在输出之后, p 将会指向数组中的下一个元      素 -2 ,但这个变化不影响已经输出的结果。

3.数组指针

int main()
{

	int a[][3] = { 1,-2,3,-4,5,-6 }, (*p)[3] = a + 1;
	cout << **p << endl;
	return 0;
}

https://i-blog.csdnimg.cn/direct/09d7d840a2f5413cb606b063dd2b2582.png

  • (*p)[3] = a + 1;

首先要明确的是,p是一个指针,因为加上了()。 这里定义了一个指针 p ,它指向一个包含3个整数的数组。 a + 1 指向数组 a 的第二行(即 a[1] ),因此 p 直接指向了 a 的第二行。

4.new对象要显示调用析构函数

#include<iostream>
#include<string>
using namespace std;
class Person {
private:
	int age;
public:
	string name;
	Person(int a, string n = "Hqu") {
		age = a;
		name = n;
		cout << "constructing..." << n << endl;
	}
	~Person() {
		cout << name << "being destructed!" << endl;
	}

};
int main() {
	Person* p1 = new Person(19, "BUAA");
	Person p2(50, "dong");
	return 0;
}

https://i-blog.csdnimg.cn/direct/9502ef38a2af4dc8ab855169af9f5da1.png 当你使用 new 来分配内存时,需要显式地使用 delete 来释放这块内存,否则析构函数不会被调用,导致可能的内存泄漏。

而普通构造函数,在程序结束后会自动调用析构函数。

5.前置++、后置++和短路

int main()
{
	int i = 1, j = 0;
	if (--i && j++)
	//if(j++ && --i)
		cout << i << "," << j << endl;
	else
		cout << i << "," << j << endl;

	return 0;
}

https://i-blog.csdnimg.cn/direct/2925667abfc44276942f21c89a874aa7.png

关键在于理解 if (--i && j++) 这一行。这里使用了逻辑与( && )运算符,它具有短路特性。具体来说,当且仅当左侧表达式为真(非零)时,才会计算右侧表达式。在这个例子中:

  1. --i :首先执行,将 i 从1减到0。此时 --i 的结果是0。
  2. 因为 --i 的结果是0(即假),根据 && 运算符的短路特性,程序不会计算 && 后面的 j++ 部分,因为无论 j++ 的结果是什么,整个表达式的最终结果都已经是假了(只要 && 左边的操作数为假,整个表达式就为假)。

因此,在这个特定情况下,尽管 if 语句的条件判断导致执行了 else 分支,但由于 j++ 并未被执行(由于短路), j 的值并不会增加。所以,输出结果仍然是:0,0

int main()
{
	int i = 1, j = 0;
	//if (--i && j++)
	if(j++ && --i)
		cout << i << "," << j << endl;
	else
		cout << i << "," << j << endl;

	return 0;
}

https://i-blog.csdnimg.cn/direct/b88710a18a1e481bad9c5ac691044c53.png

调整了条件判断的顺序,现在逻辑表达式是 if(j++ && --i) 。这个变化很重要,因为它影响了短路求值的行为和操作符的执行情况。

  1. 首先定义两个整数变量 ij ,并分别初始化为 10
  2. if 语句中,首先计算 j++ 。这里需要注意的是, j++ 是后置递增操作,意味着它会先使用 j 的当前值(这里是 0 )进行逻辑与运算,然后再将 j 增加1。
  3. 因为 j++ 使用的是 j 的原始值 0 ,所以 j++ && --i 这个表达式的左部分已经是 false (即0)。根据逻辑与运算的短路特性,一旦确定左边为假,则不会评估右边的表达式。因此, --i 不会被执行。
  4. 尽管 --i 没有被执行,但由于 j++ 的存在, j 的值会在这一行结束后增加到 1

因此,程序将会输出 ij 的值,其中 i 保持不变,因为 --i 没有被执行;而 j 则由于 j++ 而从 0 变成了 1 。最终输出结果将是:1,1