天天看點

交叉引用的解決方法——類聲明的應用

交叉引用的解決方法

什麼是交叉引用?

什麼是交叉引用?一言以蔽之,就是: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. 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中。

代碼例如以下:

#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;        //全部的頁
};           
#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;
}           
#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;    //文本單元對象
};           
#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;
}           

繼續閱讀