两个C++类的交叉引用和同步改变
实际编程中有时会碰到两个类之间交叉引用的问题,比如一个类A含一个类B的指针成员,一个类B含类A的指针成员,两个类相互“关联”;而且更重要的是:对类B的任意修改应该同时改变A中的B指针指向的值,同理适用于类A。良好的设计应当可以通过任意一个类的接口来同时改变A、B两个对象,而不必调用两个类的对应接口。
boost::enable_shared_from_this提供了这个能力
代码:
A.h:
#pragma once #include <boost/shared_ptr.hpp> class CB; typedef boost::shared_ptr<CB> B_ptr; class CA { friend class CB; // 为了调用SetB函数 public: CA(int i):m_aInt(i){} B_ptr GetB(){ return m_bObj; } void SetI(int i){ m_aInt = i; } int GetI(){ return m_aInt; } private: // 为了防止此接口误用,让其为private // 改变关联时应通过B的接口,否则造成"a关联了b,但b没有关联a" void SetB(B_ptr pb) { m_bObj = pb; } // 这个函数只在B中关联时被调用 int m_aInt; B_ptr m_bObj; }; typedef boost::shared_ptr<CA> A_ptr;
B.h:
#pragma once #include <boost/enable_shared_from_this.hpp> #include "A.h" // 类B在概念上包含类A,通过类B的接口将同时作用于类A class CB: public boost::enable_shared_from_this<CB> { public: CB(int i):m_bInt(i){} // CB(int i, A_ptr pa):m_bInt(i){ Connect(pa); } // error! B类还没有构造完毕,就使用shared_from_this() void Connect(A_ptr pa); // 关联 void Disconnect(); // 取消关联 A_ptr GetA(){ return m_aObj; } void SetI(int i){ m_bInt = i; } int GetI(){ return m_bInt; } private: int m_bInt; A_ptr m_aObj; };
B.cpp:
#include "B.h" void CB::Connect(A_ptr pa) { m_aObj = pa; // B有一个A成员 pa->SetB(shared_from_this()); // B本身现在是A的成员 } void CB::Disconnect() { if(m_aObj) { m_aObj->SetB(B_ptr()); // A中的B成员为空 m_aObj = A_ptr(); // B中的A成员为空 } }
main.cpp:
#include <iostream> #include "B.h" void PrintInfo(A_ptr aObj, B_ptr bObj) { std::cout << "============info============" << std::endl; std::cout << "a_i: " << aObj->GetI() << std::endl; std::cout << "a_b_i: " << aObj->GetB()->GetI() << std::endl; std::cout << "b_a_i: " << bObj->GetA()->GetI() << std::endl; std::cout << "b_i: " << bObj->GetI() << std::endl; } int _tmain(int argc, _TCHAR* argv[]) { A_ptr aObj(new CA(1)); // A中的B成员是空的 B_ptr bObj(new CB(9)); // B中的A成员是空的 bObj->Connect(aObj); // 建立关联 // aObj->SetB(bObj); // error! 此句不能替代上一句 PrintInfo(aObj, bObj); aObj->SetI(11); PrintInfo(aObj, bObj); bObj->SetI(99); PrintInfo(aObj, bObj); aObj->GetB()->SetI(22); PrintInfo(aObj, bObj); bObj->GetA()->SetI(33); PrintInfo(aObj, bObj); // 改变B关联的A对象 A_ptr aObj2(new CA(2)); bObj->Disconnect(); // 释放原来的关联,A中的B成员现在是空 bObj->Connect(aObj2); // 建立新的关联 PrintInfo(aObj2, bObj); // aObj->GetB()->GetI(); // error! aObj没有与B关联 return 0; }类B中的Connect方法用于A、B对象的相互关联,在此之前先构造A、B的一个对象,A、B中的指针对象全部采用智能指针。
关联之后,可以通过A、B的任一方法同时改变关联的数据:如aObj->SetI(11);同时改变a中int和b关联的a的int
类B的Disconnect取消两者的关联,同步改变机制就不存在了,并且A中的B成员和B中的A成员都为空,除非重新Connect。
注意:在Connect之前,A、B的各自构造函数不包含智能指针成员的初始化,shared_from_this()必须在一个类构造完成之后再调用,所以在类B构造函数中调用Connect是一种错误,类A中的SetB函数单向地使一个B与其关联,这个函数只被Connect和Disconnect调用,为防止误用,设为private,则B是A的友元
一种优化可以是:只将Connect和Disconnect设为类A的友元,防止类B的其他成员函数对它的误用
这样修改:
A.h:
#pragma once #include "B.h" class CA { friend void CB::Connect(A_ptr pa); // 为了调用SetB函数 friend void CB::Disconnect(); // 为了调用SetB函数 ..... }
B.h:
#include <boost/shared_ptr.hpp> #include <boost/enable_shared_from_this.hpp> class CA; typedef boost::shared_ptr<CA> A_ptr; // 类B在概念上包含类A,通过类B的接口将同时作用于类A class CB: public boost::enable_shared_from_this<CB> { ..... }; typedef boost::shared_ptr<CB> B_ptr;
A.cpp: 将B的成员Connect和Disconnect从B.cpp中移入
#include "A.h" // A的成员函数定义在此 // A的友元函数(B的两个成员函数)定义在此 void CB::Connect(A_ptr pa) { ..... } void CB::Disconnect() { ...... }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。