C++中的友元模版
2022-05-21 本文已影响0人
左图右码
友元在C++中意味着不好的设计,因忽略访问属性的限制,破坏了封装性。我却很喜欢这个特性。
特别说明,这个特性适合库作者,一般的使用者可能意义不大,尤其是作为代码的搬运工。
在非模版的设计中,有些时候,在写目标类的时候尚不能确定那个类成为友元类,只能后期增加友元类的声明,这就导致目标类的代码不能封闭。模版可以解决这个问题,将友元类的设置延后到编译阶段。
比如有个日志类,有私有的方法和私有数据成员。想要直接访问私有数据成员,除了用公开的get/set外,就只能用友元了。通过模版,这个myLog类的代码就能封闭了。
template<typename T>
class myLog
{
friend T;
std::string str;
void print()
{
std::cout << str << std::endl;
}
public:
myLog() = default;
};
class writeDataToLog
{
private:
using Log = myLog<writeDataToLog>;
Log _log;
public:
void setval(std::string data) {
_log.str = data;
}
void print()
{
_log.print();
}
};
重要的就是friend T;
这一句,在writeDataToLog里就可以直接变身为myLog的友元,调用私有的变量和函数。
如果T是普通类型如int,double等,这一句是被编译器忽略的,不会有副作用。所以如果定义一个别名来直接使用类型无关的myLog:
using myLog_t = myLog<void>;
myLog_t log;
如果还不能说明友元模版的必要性,我们看看另外一个例子(重点在第14行),这是个使用引用计数的智能指针的片段:
template<typename T>
class SmartPointer
{
T* pointer = nullptr;
ReferenceCount* refcount = nullptr;
//友元模板,让其它类型的智能指针可以直接访问私有变量
template<typename>
friend class SmartPointer;
//...
//有继承关系的构造,向上转换,U继承自T
template<typename U, typename = std::enable_if_t<std::is_base_of_v<T, U>>>
SmartPointer(SmartPointer<U> const& other)
{
if (pointer = other.pointer, refcount = other.refcount) //友元模板的设置才能访问other的私有变量
{
refcount->increment();
}
}
//...
}
如果像让不同的每个myLog的实例类都能互相访问私有数据,则可以这么做:
template<typename T>
class myLog
{
//...
template<typename>
friend class myLog;
//...
}
这样,myLog家族类之间就没有秘密了。仔细琢磨,这挺神奇的。