友元
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以
友元不宜多用。
友元分为友元函数和友元类
友元函数
什么是友元函数:类的友元函数是定义在类外部的普通函数,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数,不属于任何类,但需要在类的内部声明,声明时需要加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修饰(指不能卸载函数后面,因为只要成员函数才能那样写,那样写是修改this指针的,但是可以写在前面,这样是修饰返回值的)
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
- 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数的调用原理相同
友元类
类似地,像一个友元函数一样,一个类也可以使用关键字friend成为另一个类的友元类。例如:
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;
};
注意,在Time的友元类Date中定义了
Time _t;
才可以访问,不能直接指定某个域直接访问,要定义出来才行。例如Date要访问Time中的某个成员变量,但是没有定义对象,那就无处去访问。加入要访问成员函数,但是我们编译器不知道成员函数中是否改变了成员变量,所以无法让其访问。必须要在Date中将Time实例化了才可以访问Time的成员。
- 友元关系是单向的,不具有交换性。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接
访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。- 友元关系不能传递。
如果C是B的友元, B是A的友元,则不能说明C时A的友元。- 友元关系不能继承,在继承位置再详细介绍。
内部类
概念: 如果一个类定义在另一个类的内部,这个内部类就叫做内部类。 内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越
的访问权限。
注意: 内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
特性1:外部类与内部类不存在包含关系
class A
{
public:
class B
{
public:
void test()
{
}
private:
int _b;
};
private:
int _a;
};
int main()
{
cout << sizeof(A) << endl;
return 0;
}
内部类很容易被理解为外部的类的大小计算时要包含内部类的大小,其实并不是,不存在任何的包含关系,如下是执行结果。
特性2:内部类与外部类的关系是内部类受到外部类的访问限制,这点与静态有点类似。内部类是全局的,只是访问的时候受到了类域的限制。
int main()
{
cout << sizeof(A) << endl;
A aa;
A::B bb;
bb.test();//当定义出来以后就可以访问了。
return 0;
}
如上述,bb在定义的时候要受到类域和访问限定符的限制。要通过A来定义bb对象。
如果定义为私有的内部类,上述的写法就会报错了。在外部是无法访问的。
此时只能在外部类的里面创建内部类的对象。
class A
{
public:
void creat()
{
B bb;
}
private:
class B
{
private:
int _b;
};
private:
B bbb//如果不在函数内部,那么前面要声明了才能创建
int _a;
};
int main()
{
cout << sizeof(A) << endl;
A aa;
// A::B bb;
return 0;
}
特性3:内部类可以访问外部类的私有和公有,但是外部类只可以访问内部类的公有,但是不能访问内部类的私有。即内部类天生是外部类的友元。
class A
{
public:
void creat(int i)
{
B bb;
bb.func2(i);//外部类通过调用内部类的公有函数改变内部类的值
}
//B bbb//不能直接创建,只能在函数里面创建
private:
class B
{
public:
void func1(A* pa)
{
pa->_a++;//内部类直接访问外部类的私有
}
void func2(int i)
{
_b += i;
}
private:
int _b;
};
private:
int _a;
};
int main()
{
cout << sizeof(A) << endl;
A aa;
return 0;
}
内部类和友元函数并不经常用,且两者都破坏了代码的独立性,所以不需要了解特别深。
匿名对象
匿名对象就是没有名字的对象,最大的特点就是声明周期只在定义的那一行
匿名对象会经常使用,使用场景如下是一种情况
class Date
{
public:
void func()
{
cout << "test" << endl;
}
private:
int date;
};
int main()
{
Date().func();
return 0;
}
如上,只为了执行Date里面的func1()函数。