首页 > 编程语言 >【C++】两个类的相互引用_c++ 类相互引用

【C++】两个类的相互引用_c++ 类相互引用

时间:2024-05-23 23:19:55浏览次数:13  
标签:include bb yngzmiao c++ cpp 引用 相互 main class

有时候在设计数据结构的时候,可能会遇到两个类需要相互引用的情形。比如类 A 有类型为 B 的成员,而类 B 又有类型为 A 的成员。

那么这种情形下,两个类的设计上需要注意什么呢?

同一文件

尝试方案

将 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++11
main.cpp:9:5: error: ‘B’ does not name a type
     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() {

  return 0;
}

编译这一段代码,编译仍然出错:

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

编译时出现 "field has incomplete type",通常的错误原因为:类或结构体的前向声明只能用来定义指针对象或引用,因为编译到这里时还没有发现定义,不知道该类或者结构的内部成员,没有办法具体的构造一个对象,所以会报错。

解决办法:将类成员改成指针就好了。程序中使用 incomplete type 实现前置声明,有助与实现数据类型细节的隐藏。

按照这个办法来进行修改,将 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;
}

编译这一段代码,编译顺利,没有问题。运行这段代码:

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

又有问题了,tmp1.b_->bb_的打印结果为 H。这个问题很容易检查出问题:在类 A 的定义中,定义了指向类型 B 的指针 b_,但是并没有对该指针分配内存空间,当然会有一些奇怪的值打印出来。可以修改为:

#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;
}

编译又出现问题了:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11
main.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 的结构还不知道,怎么能 new 出来呢?

最终代码

如果想要获得正确的代码,不能将 new 的操作放在构造函数中,放在其他地方手动创建即可:

#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;
}

编译并运行这段代码:

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

原因分析

类 A 和类 B 相互引用比较麻烦的根本原因在于:定义 A 的时候,A 的里面有 B,所以就需要去查看 B 的占空间大小,但是查看的时候又发现需要知道 A 的占空间大小,从而造成死循环

不同文件

不同文件指的是:类型 A 定义在 A.h 文件,类型 B 定义在 B.h 文件,同时在 main.cpp 中创建 A、B 类型的对象进行输出。通过上文的一些经验,可能还需要 create() 函数来对 B 指针进行 new 操作。

由于不同文件的写法,坑比较多,接下来就直接给出正确的代码内容。

在 A.h 文件中,定义类 A:

#ifndef A_H
#define A_H

class B;                // highlight 1

class A {
  public:
    A() {
      aa_ = 'A';
    }
    void create();
    char aa_;
    B *b_;
};

#endif

在 A.cpp 文件中,定义类 A 方法的实现:

#include "B.h"        // highlight 2

void A::create() {
 b_ = new B();
}

在 B.h 文件中,定义类 B:

#ifndef B_H
#define B_H

#include "A.h"        // highlight 3

class 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;
}

对这一段代码中的坑进行罗列:

  1. A.h 中包含 B.h 且 B.h 中包含 A.h,头文件不能循环 include。需要在定义非指针类的那个. h 文件 include 另一个;而定义指针类的那个. h 文件需要使用前置声明
  2. create() 函数不能在. h 文件中进行定义,因为在该函数中需要进行 new 操作,而该操作需要又另一个类的完整定义,即需要 include。由于第一点原因,只好在. cpp 文件中进行方法的实现

总结

两个类相互引用,一个用对象、include;另一个用指针、前置声明、create 手动 new。手动 new 的过程不能在构造函数中进行,同时需要知道另一个类的完整定义(include)

注意:本文所举例的部分都没有对 new 出来的空间进行 delete 操作,会引发内存泄漏。这部分需要读者自行补充。

相关阅读

标签:include,bb,yngzmiao,c++,cpp,引用,相互,main,class
From: https://www.cnblogs.com/viewoverlooking/p/18209556

相关文章

  • C++Linux系统编程——文件和目录操作函数
    stat函数(重要)#include<sys/types.h>#include<sys/stat.h>#include<unistd.h>​intstat(constchar*path,structstat*buf);intlstat(constchar*pathname,structstat*buf);功能: 获取文件状态信息 stat和lstat的区别:   当文件是一个符号......
  • 【C++初阶】—— 类和对象 (上)
    ......
  • C++基础知识学习笔记(2)
    资料来源https://www.bilibili.com/video/BV1et411b73Z/?spm_id_from=333.337.search-card.all.click&vd_source=cc561849591f6a210152150b2493f6f3简单知识点指针作用:通过指针间接访问内存可以通过指针来保存一个地址。指针所占内存空间32位操作系统,占4个字节。64位操......
  • 深入理解C++多态-虚函数
    引言C++多态的实现方式可以分为静态多态和动态多态,其中静态多态主要有函数重装和模板两种方式,动态多态就是虚函数。下面我们将通过解答以下几个问题的方式来深入理解虚函数的原理:为什么要引入虚函数?(用来解决什么问题)虚函数底层实现原理使用虚函数时需要注意什么?正文......
  • 在C++中,将类的成员函数(也称为方法)作为参数传递
    在C++中,你可以将类的成员函数(也称为方法)作为参数传递,但这通常涉及到使用函数指针或者更现代的C++11及以后版本的std::function和lambda表达式。不过,更常见的是传递成员函数指针,但请注意,成员函数指针与常规函数指针在语法和使用上有所不同,因为成员函数需要访问类的特定实例(即对象)。......
  • 关于QT的头文件相互包含的问题
    就是我有两个代码,head_test.cpp,head_test.h和head_test2.cpp,head_test2.h。要互相调用对方的类。为了方便看下面我就用A代表head_test,B代表head_test21.head_test.h2.head_test.cpp3.head_test2.h4.head_test2.cpp这两天我在查网上的资料,基本上都是说,需要在头文件中......
  • 单例模式c++实现
    单例模式是一种创建型设计模式,它保证一个类仅有一个实例,并提供一个全局访问点来访问这个唯一实例。下面是一个简单的C++实现单例模式的例子:cppincludeincludeclassSingleton{private:staticSingleton*instance;staticstd::mutexmtx;Singleton(){}//私有构造函......
  • c++ 语法 引用
      引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。C++引用vs指针引用很容易与指针混淆,它们之间有三个主要的不同:不存在空引用。引用必须连接到一块合法的内存。一旦引用被初始化为......
  • C++ 的 mutable 引出的一系列思考
    阅读多线程实战第六章第二节时,看到mutable关键词的使用,突然忘记它的含义=>  https://github.com/xiaoweiChen/CPP-Concurrency-In-Action-2ed-2019/blob/master/content/chapter6/6.2-chinese.md 进而引申到mutable的使用=> C++的mutable关键字......
  • Linux C++ IDE
    在linux下开发C++一般使用cmake,而我们也需要一个IDE来提高开发效率,之前使用过VSCode,这个需要装各种插件进行许多配置才能成为一个真正的IDE,后来知道了Clion是一个更好的选择。那么我们怎么使用呢?官网下载安装包,可以试用一个月,然后我们可以去淘宝买个共享帐号,我......