两个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()
{
	......
}

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。