C++中的friend函数详细解析(二)
一.设计模式
- 单例模式(类只能生成一个对象)
- 屏蔽构造函数,拷贝构造函数(放在private中 类外无法访问),此时类外就无法看到这两个函数了 也就无法生成对象了 类就没有意义了
- 类中给出一个接口(getInstance) 生成唯一对象 返回值为类类型的指针或引用(不生成其他对象) 如果为类类型返回 则会有临时对象产生 此时就不叫单例模式了
①返回值不能返回类类型(会有临时对象产生)
②这个接口一定要不依赖对象(因为这个接口是要生成对象的 如果依赖对象 则无法生成对象) 因此要加static关键字
代码如下:
class SingleTon { public: static SingleTon* getInstance()//类中给出一个接口getInstance生成唯一对象返回值为类类型的指针或引用 同时这个接口不依赖对象因此加上static的关键字 { return &single; } private: SingleTon(){};//屏蔽构造函数(防止生成新对象) SingleTon(const SingleTon&);//屏蔽拷贝构造函数(防止生成新对象) static SingleTon single; }; SingleTon SingleTon::single;//static修饰的成员要在类外初始化 int main() { SingleTon* psingle1 = SingleTon::getInstance(); SingleTon* psingle2 = SingleTon::getInstance(); SingleTon* psingle3 = SingleTon::getInstance(); SingleTon* psingle4 = SingleTon::getInstance(); return 0; }
线程安全问题
- 此时线程不安全则要加锁
class SingleTon { public: static SingleTon* getInstance() { //lock(); if (psingle == NULL) { psingle = new SingleTon(); } //unlock(); return psingle; }
此时A线程执行完了 加锁解锁后 B线程才能执行 但效率很低 不管对象是否存在都会加锁解锁
- 双重锁机制的单例模式
当有对象生成的时候 直接返回对象 就不用加锁解锁了
在main函数之前就生成对象就不存在线程安全的问题了 因为线程在main中才执行
代码如下:
class SingleTon { public: static SingleTon* getInstance() { if (psingle == NULL)//当有对象生成的时候 直接返回对象 { //lock(); if (psingle == NULL) { psingle = new SingleTon(); } //unlock(); } return psingle; } private: SingleTon(){} SingleTon(const SingleTon&); static SingleTon* psingle;//指向对象的指针 static修饰的并不属于对象 属于类作用域 }; SingleTon* SingleTon::psingle = NULL;
二.友元函数
在C++中,我们使用类对数据进行了隐藏和封装,类的数据成员一般都定义为私有成员,成员函数一般都定义为公有的,以此提供类与外界的通讯接口。但是,有时需要定义一些函数,这些函数不是类的一部分,但又需要频繁地访问类的数据成员,这时可以将这些函数定义为该函数的友元函数。除了友元函数外,还有友元类,两者统称为友元。友元的作用是提高了程序的运行效率(即减少了类型检查和安全性检查等都需要时间开销),但它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。
友元类 :将外界的某个类在本类别的定义中说明为友元,那么外界的类就成为本类的“朋友”,那个类就可以访问本类的私有数据了。友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。
当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。定义友元类的语句格式如下:
friend class 类名;
其中:friend和class是关键字,类名必须是程序中的一个已定义过的类。
例如,以下语句说明类B是类A的友元类:
class A { … public: friend class B; … };
经过以上说明后,类B的所有成员函数都是类A的友元函数,能存取类A的私有成员和保护成员。
使用友元类时注意:
(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明
《windows环境多线程编程原理与应用》中解释:
如果将类的封装比喻成一堵墙的话,那么友元机制就像墙上了开了一个门,那些得
到允许的类或函数允许通过这个门访问一般的类或者函数无法访问的私有属性和方
法。友元机制使类的封装性得到消弱,所以使用时一定要慎重。
https://www.pianshen.com/article/7773114476/
友元关系有三种:
- 函数友元 (cdecal)
- 类友元 (thiscall)
- 类成员方法的友元
只有你赋予某个类为你的友元时,那个类才有访问你的私有数据的权利。说明一个函数为一个类的友元函数则该函数可以访问此类的私有数据和方法。定义方法是在类的定义中,在函数名前加上关键字friend.
友元函数的优缺点:
优点:打破了封装性 通常对于普通函数来说,要访问类的保护成员是不可能的,如果想这么做那么必须把类的成员都生命成为public(共用的),然而这做带来的问题遍是任何外部函数都可以毫无约束的访问它操作它,c++利用friend修饰符,可以让一些你设定的函数能够对这些保护数据进行操作,避免把类成员全部设置成public,最大限度的保护数据成员的安全。
缺点:友元能够使得普通函数直接访问类的保护数据,避免了类成员函数的频繁调用,可以节约处理器开销,提高程序的效率,但所矛盾的是,即使是最大限度大保护,同样也破坏了类的封装特性,这即是友元的缺点,在现在cpu速度越来越快的今天我们并不推荐使用它,但它作为c++一个必要的知识点,一个完整的组成部分,我们还是需要讨论一下的。 在类里声明一个普通数学,在前面加上friend修饰,那么这个函数就成了该类的友元,可以访问该类的一切成员。
因此建议:能不用友元就不用友元 不得不用的时候在用友元。