交叉引用的解决方法——类声明的应用
2015-07-12 23:00
399 查看
交叉引用的解决方法
什么是交叉引用?
什么是交叉引用?一言以蔽之,就是:A类中包含B类的对象,B类中包含A类的对象。以一场景为例
我们先来看一个场景。假设有一个电子文档(Document)、一个文档下有多个页(Page),每个页下有多个文本单元(TextUnit,表示文档内元素的基本单位),一个文档中的所有文本单元对象都有唯一的ID。这样每创建一个文本单元时都要为其设置一个唯一的ID,我们在Document类中就需要一个生成唯一ID的方法为所有的文本单元创建唯一标识。于是我们就会有下面的类关系设计图:图1 :类的关系图
于是我们想当然的会有这样的代码:
TextUnit.h:
#pragma once class TextUnit { public: TextUnit(void); TextUnit(int id); ~TextUnit(void); public: int GetId() { return m_id; } void SetId(int id) { m_id = id; } private: int m_id; //文本对象的唯一标识 };
TextUnit.cpp:
#include "StdAfx.h" #include "TextUnit.h" TextUnit::TextUnit(void) { } TextUnit::TextUnit( int id ) : m_id(id) { } TextUnit::~TextUnit(void) { }
Page.h:
#pragma once #include <vector> #include "TextUnit.h" #include "Document.h" typedef std::vector<TextUnit*> VecTextUnit; class Page { public: Page(Document* pDocument); ~Page(void); public: //添加一个文本单元 TextUnit* AddTextUnit(); private: Document* m_pDocument; //文档对象,用于获得ID VecTextUnit* m_pvecTextUnits; //文本单元对象 };
Page.cpp:
#include "StdAfx.h" #include "Page.h" Page::Page(Document* pDocument) : m_pDocument(pDocument), m_pvecTextUnits(new VecTextUnit) { } Page::~Page(void) { for (VecTextUnit::iterator iter = m_pvecTextUnits->begin(); iter != m_pvecTextUnits->end(); ++ iter ) { delete[] *iter; *iter = NULL; } m_pvecTextUnits->clear(); delete[] m_pvecTextUnits; m_pvecTextUnits = NULL; } TextUnit* Page::AddTextUnit() { int id = m_pDocument->GenerateId(); TextUnit* pTextUnit = new TextUnit(id); if (pTextUnit == NULL) { return NULL; } m_pvecTextUnits->push_back(pTextUnit); return pTextUnit; }
Document.h:
#pragma once #include <vector> #include "Page.h" typedef std::vector<Page*> VecPage; class Document { public: Document(void); ~Document(void); public: //生成本文档内唯一的文本对象ID int GenerateId(); //添加一页 Page* AddPage(); private: static int s_id; //用于生成唯一的ID VecPage* m_pvecPages; //所有的页 };
Document.cpp:
#include "StdAfx.h" #include "Document.h" int Document::s_id = 0; Document::Document(void) : m_pvecPages(new VecPage) { } Document::~Document(void) { for (VecPage::iterator iter = m_pvecPages->begin(); iter != m_pvecPages->end(); ++ iter ) { delete[] *iter; *iter = NULL; } m_pvecPages->clear(); delete[] m_pvecPages; m_pvecPages = NULL; } int Document::GenerateId() { return s_id ++; } Page* Document::AddPage() { Page* pPage = new Page(this); if (pPage == NULL) { return NULL; } m_pvecPages->push_back(pPage); return pPage; }
编译
好,代码写完了,我们对它进行编译,这时你会发现一堆的错误:1>d:\博客文章\c++高级编辑\projects\crossreference\crossreference\document.h(6): error C2065: ‘Page’ : undeclared identifier
1>d:\博客文章\c++高级编辑\projects\crossreference\crossreference\document.h(6): error C2059: syntax error : ‘>’
1>d:\博客文章\c++高级编辑\projects\crossreference\crossreference\document.h(9): error C2143: syntax error : missing ‘;’ before ‘{’
…
1>d:\博客文章\c++高级编辑\projects\crossreference\crossreference\page.h(12): error C2061: syntax error : identifier ‘Document’
1>d:\博客文章\c++高级编辑\projects\crossreference\crossreference\page.h(20): error C2143: syntax error : missing ‘;’ before ‘*’
1>d:\博客文章\c++高级编辑\projects\crossreference\crossreference\page.h(20): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>d:\博客文章\c++高级编辑\projects\crossreference\crossreference\page.h(20): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
…
原因分析
这是因为1. C++中,在创建或使用一个类时,这个类必须被定义完整(就是一个完整的类型);
2. 类的定义也可以和函数一样分成两步,先声明后定义。
class T; //声明一个类
这种声明也被称为前向声明,在程序中引入名字T,并指明T是一种类类型。此时的T在它被定义之前是不完整的类型(incomplete type),也就是说只知道它是一种类类型,并不知道它有哪些成员,但可定义这个类型(T)的指针和引用。
类的定义:
class T { // todo: 定义类的成员(属性和方法) };
只有类(T)定义完成,它才是一个完整的类型,才是可见的(才可被创建和使用)。
而我们的程序现在就出现这种很有意思的状态:在定义Document时,发现Page还没有定义完整(Document中有Page类型的成员);在定义Page的时候发现Document还没有定义完整(Page中有Document类型的指针对象)。也就是说Document不知道Page,Page不知道Docunent,这时就像两只狗打架,A狗咬着B狗的尾巴,B狗咬着A狗的尾巴。
解决方法
1.在Document.h文本中加入Page类的声明:calss Page; 把include “Page.h”放到Document.cpp中。2.Page.h文本中加入Page类的声明:calss Document; 把include “Document.h”放到Page.h中。
代码如下:
Document.h:
#pragma once #include <vector> class Page; typedef std::vector<Page*> VecPage; class Document { public: Document(void); ~Document(void); public: //生成本文档内唯一的文本对象ID int GenerateId(); //添加一页 Page* AddPage(); private: static int s_id; //用于生成唯一的ID VecPage* m_pvecPages; //所有的页 };
Document.cpp:
#include "StdAfx.h" #include "Document.h" #include "Page.h" int Document::s_id = 0; Document::Document(void) : m_pvecPages(new VecPage) { } Document::~Document(void) { for (VecPage::iterator iter = m_pvecPages->begin(); iter != m_pvecPages->end(); ++ iter ) { delete[] *iter; *iter = NULL; } m_pvecPages->clear(); delete[] m_pvecPages; m_pvecPages = NULL; } int Document::GenerateId() { return s_id ++; } Page* Document::AddPage() { Page* pPage = new Page(this); if (pPage == NULL) { return NULL; } m_pvecPages->push_back(pPage); return pPage; }
Page.h:
#pragma once #include <vector> #include "TextUnit.h" class Document; typedef std::vector<TextUnit*> VecTextUnit; class Page { public: Page(Document* pDocument); ~Page(void); public: //添加一个文本单元 TextUnit* AddTextUnit(); private: Document* m_pDocument; //文档对象,用于获得ID VecTextUnit* m_pvecTextUnits; //文本单元对象 };
Page.cpp:
#include "StdAfx.h" #include "Page.h" #include "Document.h" Page::Page(Document* pDocument) : m_pDocument(pDocument), m_pvecTextUnits(new VecTextUnit) { } Page::~Page(void) { for (VecTextUnit::iterator iter = m_pvecTextUnits->begin(); iter != m_pvecTextUnits->end(); ++ iter ) { delete[] *iter; *iter = NULL; } m_pvecTextUnits->clear(); delete[] m_pvecTextUnits; m_pvecTextUnits = NULL; } TextUnit* Page::AddTextUnit() { int id = m_pDocument->GenerateId(); TextUnit* pTextUnit = new TextUnit(id); if (pTextUnit == NULL) { return NULL; } m_pvecTextUnits->push_back(pTextUnit); return pTextUnit; }
相关文章推荐
- HT for Web列表和3D拓扑组件的拖拽应用
- Xcode histogram图像显示测试
- OLAP 支持---ROLLUP和CUBE语句 group by 的增强版本语句!可进行分层分组模式外 (小计与总计)
- android入门学习-UML(统一建模语言)介绍
- SQL Server ->> 分区表上创建唯一分区索引
- Section 1.3
- 适配器模式Adapter、外观模式Facade-- 学习HeadFirst设计模式记录
- Lowest Common Ancestor of a Binary Search Tree
- 对比iOS中的四种数据存储
- 【PHP教程二】PHP基本知识,运行自己写的.php文件
- mysql必知必会(六)
- 优化Mysql数据库的8个方法
- Thread
- 实时获取UITextField内容
- gpio
- linux下mysql安装和安装遇到的问题
- python用for循环实现冒泡排序
- lightoj 1021 Painful Bases (状压dp)
- 转:CocoaPods pod install/pod update更新慢的问题
- Spring boot 学习笔记 ---分分钟构建一个web程序(一)