午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

【C 】兩個類的相互引用

 芥子c1yw3tb42g 2024-10-19

有時候在設(shè)計數(shù)據(jù)結(jié)構(gòu)的時候,可能會遇到兩個類需要相互引用的情形。比如類 A 有類型為 B 的成員,而類 B 又有類型為 A 的成員。

那么這種情形下,兩個類的設(shè)計上需要注意什么呢?

同一文件

嘗試方案

將 A 和 B 的定義都放在一個文件中,例如:

#include <iostream>class A {  public:    A() {
      aa_ = 'A';
    }    char aa_;
    B b_;
};class B {  public:    B() {
      bb_ = 'B';
    }    char bb_;
    A a_;
};int main() {  return 0;
}

編譯這一段代碼,編譯出錯:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11main.cpp:9:5: error: 'B’ does not name a type
     B b_;
     ^

編譯的報錯提示,B 不是一個數(shù)據(jù)類型??赡苣銜?,會不會前置聲明一下就可以了?即將代碼修改為:

#include <iostream>class B;class A {  public:    A() {
      aa_ = 'A';
    }    char aa_;
    B b_;
};class B {  public:    B() {
      bb_ = 'B';
    }    char bb_;
    A a_;
};int main() {  return 0;
}

編譯這一段代碼,編譯仍然出錯:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11main.cpp:11:7: error: field 'b_’ has incomplete type
     B b_;
       ^

編譯時出現(xiàn) 'field has incomplete type',通常的錯誤原因?yàn)椋?strong>類或結(jié)構(gòu)體的前向聲明只能用來定義指針對象或引用,因?yàn)榫幾g到這里時還沒有發(fā)現(xiàn)定義,不知道該類或者結(jié)構(gòu)的內(nèi)部成員,沒有辦法具體的構(gòu)造一個對象,所以會報錯。

解決辦法:將類成員改成指針就好了。程序中使用 incomplete type 實(shí)現(xiàn)前置聲明,有助與實(shí)現(xiàn)數(shù)據(jù)類型細(xì)節(jié)的隱藏。

按照這個辦法來進(jìn)行修改,將 b_的類型由 B 的對象修改成指向類型 B 的指針:

#include <iostream>class B;class A {  public:    A() {
      aa_ = 'A';
    }    char aa_;
    B *b_;
};class B {  public:    B() {
      bb_ = 'B';
    }    char bb_;
    A a_;
};int main() {
  A tmp1;
  std::cout << tmp1.aa_ << ' ' << tmp1.b_->bb_ << std::endl;
  B tmp2;
  std::cout << tmp2.bb_ << ' ' << tmp2.a_.aa_ << std::endl;  return 0;
}

編譯這一段代碼,編譯順利,沒有問題。運(yùn)行這段代碼:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11yngzmiao@yngzmiao-virtual-machine:~/test$ ./main 
A H
B A

又有問題了,tmp1.b_->bb_的打印結(jié)果為 H。這個問題很容易檢查出問題:在類 A 的定義中,定義了指向類型 B 的指針 b_,但是并沒有對該指針分配內(nèi)存空間,當(dāng)然會有一些奇怪的值打印出來。可以修改為:

#include <iostream>class B;class A {  public:    A() {
      aa_ = 'A';
      b_ = new B();
    }    char aa_;
    B *b_;
};class B {  public:    B() {
      bb_ = 'B';
    }    char bb_;
    A a_;
};int main() {
  A tmp1;
  std::cout << tmp1.aa_ << ' ' << tmp1.b_->bb_ << std::endl;
  B tmp2;
  std::cout << tmp2.bb_ << ' ' << tmp2.a_.aa_ << std::endl;  return 0;
}

編譯又出現(xiàn)問題了:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11main.cpp: In constructor 'A::A()’:
main.cpp:9:18: error: invalid use of incomplete type 'class B’
       b_ = new B();
                  ^
main.cpp:3:7: error: forward declaration of 'class B’ class B;
       ^

編譯錯誤的原因還是 incomplete type,即類型 B 的結(jié)構(gòu)還不知道,怎么能 new 出來呢?

最終代碼

如果想要獲得正確的代碼,不能將 new 的操作放在構(gòu)造函數(shù)中,放在其他地方手動創(chuàng)建即可:

#include <iostream>class B;class A {  public:    A() {
      aa_ = 'A';
    }    char aa_;
    B *b_;
};class B {  public:    B() {
      bb_ = 'B';
    }    char bb_;
    A a_;
};int main() {
  A tmp1;
  tmp1.b_ = new B();
  std::cout << tmp1.aa_ << ' ' << tmp1.b_->bb_ << std::endl;
  B tmp2;
  std::cout << tmp2.bb_ << ' ' << tmp2.a_.aa_ << std::endl;  return 0;
}

編譯并運(yùn)行這段代碼:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11yngzmiao@yngzmiao-virtual-machine:~/test$ ./main 
A B
B A

原因分析

類 A 和類 B 相互引用比較麻煩的根本原因在于:定義 A 的時候,A 的里面有 B,所以就需要去查看 B 的占空間大小,但是查看的時候又發(fā)現(xiàn)需要知道 A 的占空間大小,從而造成死循環(huán)。

不同文件

不同文件指的是:類型 A 定義在 A.h 文件,類型 B 定義在 B.h 文件,同時在 main.cpp 中創(chuàng)建 A、B 類型的對象進(jìn)行輸出。通過上文的一些經(jīng)驗(yàn),可能還需要 create() 函數(shù)來對 B 指針進(jìn)行 new 操作。

由于不同文件的寫法,坑比較多,接下來就直接給出正確的代碼內(nèi)容。

在 A.h 文件中,定義類 A:

#ifndef A_H#define A_Hclass B;                // highlight 1class A {  public:    A() {
      aa_ = 'A';
    }    void create();    char aa_;
    B *b_;
};#endif

在 A.cpp 文件中,定義類 A 方法的實(shí)現(xiàn):

#include 'B.h'        // highlight 2void A::create() {
 b_ = new B();
}

在 B.h 文件中,定義類 B:

#ifndef B_H#define B_H#include 'A.h'        // highlight 3class B {  public:    B() {
      bb_ = 'B';
    }    char bb_;
    A a_;
};#endif

最終在 main.cpp 文件中,使用類 A 和類 B:

#include 'B.h'#include <iostream>int main() {
  A tmp1;
  tmp1.create();
  std::cout << tmp1.aa_ << ' ' << tmp1.b_->bb_ << std::endl;
  B tmp2;
  std::cout << tmp2.bb_ << ' ' << tmp2.a_.aa_ << std::endl;  return 0;
}

對這一段代碼中的坑進(jìn)行羅列:

  1. A.h 中包含 B.h 且 B.h 中包含 A.h,頭文件不能循環(huán) include。需要在定義非指針類的那個. h 文件 include 另一個;而定義指針類的那個. h 文件需要使用前置聲明

  2. create() 函數(shù)不能在. h 文件中進(jìn)行定義,因?yàn)樵谠摵瘮?shù)中需要進(jìn)行 new 操作,而該操作需要又另一個類的完整定義,即需要 include。由于第一點(diǎn)原因,只好在. cpp 文件中進(jìn)行方法的實(shí)現(xiàn)。

總結(jié)

兩個類相互引用,一個用對象、include;另一個用指針、前置聲明、create 手動 new。手動 new 的過程不能在構(gòu)造函數(shù)中進(jìn)行,同時需要知道另一個類的完整定義(include)。

注意:本文所舉例的部分都沒有對 new 出來的空間進(jìn)行 delete 操作,會引發(fā)內(nèi)存泄漏。這部分需要讀者自行補(bǔ)充。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多