首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

C++当两个类相互依赖时如何解决类的不完整类型

当两个类相互依赖时,可以通过使用前置声明或者将类的定义放在头文件之外的方式解决类的不完整类型。

  1. 前置声明: 当两个类相互依赖时,可以在一个类的定义之前进行前置声明。前置声明使用关键字"class"加上类名来声明一个类的存在,而不提供其具体实现。这样可以让编译器知道这个类的存在,从而解决类的不完整类型问题。在需要使用该类的成员函数或成员变量时,需要包含类的完整定义。

例如:

代码语言:txt
复制
// ClassB.h
class ClassA; // 前置声明ClassA

class ClassB {
public:
    void doSomething(ClassA* obj);
};
代码语言:txt
复制
// ClassA.h
class ClassB; // 前置声明ClassB

class ClassA {
public:
    void doSomething(ClassB* obj);
};
代码语言:txt
复制
// ClassA.cpp
#include "ClassA.h"
#include "ClassB.h"

void ClassA::doSomething(ClassB* obj) {
    // 使用ClassB的成员函数或成员变量
}
代码语言:txt
复制
// ClassB.cpp
#include "ClassA.h"
#include "ClassB.h"

void ClassB::doSomething(ClassA* obj) {
    // 使用ClassA的成员函数或成员变量
}
  1. 将类的定义放在头文件之外: 当两个类相互依赖时,可以将其中一个类的定义放在头文件之外,而将其成员函数的实现放在源文件中。这样可以避免循环包含头文件导致的类的不完整类型问题。

例如:

代码语言:txt
复制
// ClassA.h
class ClassB; // 前置声明ClassB

class ClassA {
public:
    void doSomething(ClassB* obj);
};
代码语言:txt
复制
// ClassB.h
#include "ClassA.h"

class ClassB {
public:
    void doSomething(ClassA* obj);
};
代码语言:txt
复制
// ClassA.cpp
#include "ClassA.h"
#include "ClassB.h"

void ClassA::doSomething(ClassB* obj) {
    // 使用ClassB的成员函数或成员变量
}
代码语言:txt
复制
// ClassB.cpp
#include "ClassA.h"
#include "ClassB.h"

void ClassB::doSomething(ClassA* obj) {
    // 使用ClassA的成员函数或成员变量
}

这样,当需要使用类的成员函数或成员变量时,只需要包含相应的头文件即可。

总结:以上是解决C++中当两个类相互依赖时如何解决类的不完整类型的两种常见方法。无论是使用前置声明还是将类的定义放在头文件之外,都可以解决类的不完整类型问题,让两个相互依赖的类正常使用彼此的成员函数和成员变量。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

当类的泛型相关时,如何在两个泛型类之间创建类似子类型的关系呢

比如可以将一个Integer类型的对象分配给Object类型的对象,因为Object 是Integer的超类。...那么问题来了,当类的泛型相关时,如何在两个泛型类之间创建类似子类型的关系呢?例如如何让Box 和Box变得与Box有关呢?...为了搞懂这个问题,我们先来了解一下同一类型的对象是如何实现子类型化的吧。...因此当我们在传递参数时,ArrayList类型的是可以给List或者Collection传递的。 只要不改变类型参数,类型之间的子类型关系就会保留。...小结:可以通过继承泛型类或者实现接口来对其进行子类型化。 搞懂了子类型化的问题,我们回到“如何在两个泛型类之间创建类似子类型的关系“的问题。

2.9K20

C++核心准则C.148:使用dynamic_cast进行指针类型转换时,将不能发现目标类看作是有效的选项

type when failure to find the required class is considered a valid alternative C.148:使用dynamic_cast进行指针类型转换时...,将不能发现目标类看作是有效的选项 Reason(原因) The dynamic_cast conversion allows to test whether a pointer is pointing...code that can choose alternative paths depending on the results. dynamic_cast转换允许检查是否指针指向一个在其继承结构中包含给定类的多态对象...下面的例子描述的是Shape_owner的增加函数,它接受构造出来的Shape对象的所有权。对象也会在根据它们的几何属性有序加入views容器。在这个例子中,图形没有从几何属性继承。...寻找所需类的失败会导致dynamic_cast返回一个空值,而解引用一个空指针会引起无定义的行为。因此应该总是认为dynamic_cast的结果可能为空并进行检查。

95010
  • 【C++】类和对象(下):再探构造函数、类型转换、static成员、友元、内部类、匿名对象、拷贝对象时编译器的优化

    const成员变量,引用成员变量,没有默认构造的类类型变量,必须在初始化列表位置进行初始化,否则会编译报错。 先说const成员变量和引用成员变量为什么必须在初始化列表进行初始化。...,_month(month) ,_day(day + 1) ,_n(1) ,_ref(num) ,_t(1) //_t走初始化列表 {} 所以当类类型变量有默认构造函数时...我们之前说过类型转换会产生一个临时对象。 但是编译器遇到连续构造+拷贝构造时,会优化为直接构造。 再拿栈的Push举例。..._h << endl; } private: int _b = 2; }; }; int A::_k = 3; 内部类本质也是一种封装,当A类和B类紧密关联,A类实现出来主要就是给B类使用的...• 如何优化C++标准并没有严格规定,各个编译器会根据情况自行处理。

    9810

    先有Class还是先有Object?

    “鸡・蛋”问题通常都是通过一种叫“自举”(bootstrap)的过程来解决的。 “鸡蛋问题”的根本矛盾就在于假定了“鸡”或“蛋”的其中一个要先进入“完全可用”的状态。...java.lang.Object是一个Java类,但并不是java.lang.Class的一个实例。后者只是一个用于描述Java类与接口的、用于支持反射操作的类型。...是java.lang.Object的派生类,按“一般思维”,前者应该要在后者完成初始化之后才可以初始化… 事实是:这些相互依赖的核心类型完全可以在“混沌”中一口气都初始化好,然后对象系统的状态才叫做完成了...此时这些对象虽然已经分配了空间,但因为状态还不完整所以尚不可使用。然后,通过这些分配好的空间把这些核心类型之间的引用关系串好。 到此为止所有动作都由JVM完成,尚未执行任何Java字节码。...在HotSpot VM里,有一个叫做“Universe”的C++类用于记录对象系统的总体状态。

    23120

    设计模式之原型模式(Prototype 模式)

    这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。...主要解决:在运行期建立和删除原型。 何时使用: 当一个系统应该独立于它的产品创建,构成和表示时。 当要实例化的类是在运行时刻指定时,例如,通过动态装载。...为了避免创建一个与产品类层次平行的工厂类层次时。 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。...如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。 优点: 1、性能提高。 2、逃避构造函数的约束。...模式重在产生多个相互依赖类的对象,而 Prototype 模式重在从自身复制自己创建新类。

    37510

    c++中两个类互相引用的问题

    最近在改一个C++程序的时候碰到一条警告信息,警告信息为:“                 删除指向不完整“Q2DTorusNode”类型的指针;没有调用析构函数                ...程序的变化     此时如果class A和class B相互保持对方类型的成员会如何呢?        ...解决方案: 此种状况的解决利用前置声明定义的那个类中的保持另外一个类的引用定义为指针,定义指针时不需要对那个类的定义可见。...“warning C4150: 删除指向不完整“B”类型的指针;没有调用析构函数”       而且另外的一个问题是在该.h文件中不能使用该指针调用这个类的成员,原因也是定义不可见。                ...“error C2227: “->haha”的左边必须指向类/结构/联合/泛型类型” 解决方案:       此时需要将A.h的所有成员函数实现重新定义一个.cpp文件,然后该.cpp文件去#include

    1.3K20

    Spring基础

    容器通过读取配置元数据获取关于实例化、配置和组装对象的指令。配置元数据可以用 XML、 Java 注释或 Java 代码表示,它允许用户表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系。...默认按照字段的名称(byName)去Spring容器中找依赖对象,如果没有找到,退回到按照类型查找。Spring 三级缓存为了解决以来循环问题,Spring使用了三级缓存技术。...>> singletonFactories = new HashMap(16);那么他们三个是如何解决循环依赖问题的呢?...分析上面场景时发现,要单纯解决循环依赖,其实只要有两级缓存就够了,那么为什么要用三级缓存呢?答案是因为Spring需要支持AOP。...因为Spring事务是由AOP机制实现的,也就是说从Spring IOC容器获取bean时,Spring会为目标类创建代理,来支持事务的。

    10510

    c++中两个类互相引用的问题

    最近在改一个C++程序的时候碰到一条警告信息,警告信息为:“                 删除指向不完整“Q2DTorusNode”类型的指针;没有调用析构函数                ...程序的变化     此时如果class A和class B相互保持对方类型的成员会如何呢?        ...解决方案: 此种状况的解决利用前置声明定义的那个类中的保持另外一个类的引用定义为指针,定义指针时不需要对那个类的定义可见。...“warning C4150: 删除指向不完整“B”类型的指针;没有调用析构函数”       而且另外的一个问题是在该.h文件中不能使用该指针调用这个类的成员,原因也是定义不可见。                ...“error C2227: “->haha”的左边必须指向类/结构/联合/泛型类型” 解决方案:       此时需要将A.h的所有成员函数实现重新定义一个.cpp文件,然后该.cpp文件去#include

    1.2K20

    c++中两个类互相引用的问题

    最近在改一个C++程序的时候碰到一条警告信息,警告信息为:“                 删除指向不完整“Q2DTorusNode”类型的指针;没有调用析构函数                ...程序的变化     此时如果class A和class B相互保持对方类型的成员会如何呢?        ...解决方案: 此种状况的解决利用前置声明定义的那个类中的保持另外一个类的引用定义为指针,定义指针时不需要对那个类的定义可见。...“warning C4150: 删除指向不完整“B”类型的指针;没有调用析构函数”       而且另外的一个问题是在该.h文件中不能使用该指针调用这个类的成员,原因也是定义不可见。                ...“error C2227: “->haha”的左边必须指向类/结构/联合/泛型类型” 解决方案:       此时需要将A.h的所有成员函数实现重新定义一个.cpp文件,然后该.cpp文件去#include

    1.9K50

    非局部静态数据在多编译单元中的窘境

    综上所言,本文的标题的含义是:如果在多文件中,分别定义了多个静态数据(不含局部变量),那么他们之间的相互依赖关系将会出现微妙的窘境。 什么窘境呢?...事情是这样的,由于静态数据会在程序运行开始时刻进行初始化(不管是指定初始化,还是系统自动初始化),并且C++标准没有规定多个文件中的这些静态数据的初始化次序,这就会带来一个问题:如果非局部静态数据相互依赖...BMW.startup(); // 使用car对象 } 很快,Rose的代码便会遇到灾难性的后果,因为C++编译时无法保证在MF对象初始化之时,汽车对象BMW究竟有没有初始化完毕。...整体而言,用户Rose在使用car对象的过程是完全一样的,但程序的逻辑大有不同,当Rose首次调用函数BMW的时候,局部静态对象c被创建并初始化,这保证了调用startup()函数的正确性,其次,如果startup...通过这样的设计,我们反手一勾拳同时解决了两个问题:既保证了初始化的次序,由提高了程序的性能。

    79420

    C++的new和delete详解

    当重载这两个运算符时虽然没有带static属性,但是不管如何对类的new/delete运算符的重载总是被认为是静态成员函数。...对象的自动删除技术 一般来说系统对new/delete的默认实现就能满足我们的需求,我们不需要再去重载这两个运算符。那为什么C++还提供对这两个运算符的重载支持呢?答案还是在运算符本身具有的缺陷所致。...正是因为有了对象的自动删除技术才能解决对象构造不完整时会造成内存泄露的问题。...当对象构造过程中抛出异常时,C++的异常处理机制会在特定的地方插入代码来实现对对象的delete运算符的调用,如果想要具体了解情况请参考C++对异常处理实现的相关知识点。...+对自动删除技术的支持,当CA对象在构造过程中发生异常时,我们就可以通过重载delete运算符来解决那些在构造函数中分配的数据成员内存但又不会调用析构函数来销毁的数据成员的内存问题。

    1.1K50

    C++从静态类型到单例模式

    一个很简单的例子,假设我们实现了很多函数: void FunA() {} void FunB() {} void FunC() {} 这些函数如果具有相关性,都是某个类型的工具函数,那么我们可以将其封装成一个工具类...不仅如此,使用类的静态数据成员还会遇到一个相互依赖的问题,如参考文献2中所述。...实现 C++并没有静态类和静态构造函数的概念。在参考文献1中,论述了一些用C++去实现静态构造函数,从而更加合理的去初始化静态数据成员的办法。...构造函数时私有的,所以无法直接声明和定义。 拷贝构造函数和赋值构造函数都被删除,因此无法进行拷贝和赋值。 只能通过专门的实例化函数get_instance()进行调用。...参考 C++静态构造函数 解决静态全局变量初始化的相互依赖问题 C++ 单例模式总结与剖析 C++单例模式跨DLL是不是就是会出问题?

    1.1K40

    C++一分钟之-设计模式:工厂模式与抽象工厂

    在软件工程中,设计模式是一种通用的解决方案,用于解决常见的设计问题。...常见问题与易错点 过度使用:在不需要的地方使用工厂模式会导致代码复杂度增加,维护成本上升。 违反开闭原则:当需要添加新产品时,可能需要修改现有的工厂类,这违反了“对扩展开放,对修改关闭”的原则。...如何避免 仅在需要动态选择具体实现或需要解耦创建过程时使用工厂模式。 使用抽象工厂模式来进一步封装创建过程,减少对工厂类的修改。...如何避免 在产品族之间有明确的关系,并且需要一起创建时使用抽象工厂。 确保设计足够灵活,以便在不影响其他部分的情况下添加新的产品族。...C++中更好地管理对象的创建过程,同时保持代码的清晰和可维护性。

    10010

    C++一分钟之-设计模式:工厂模式与抽象工厂

    在软件工程中,设计模式是一种通用的解决方案,用于解决常见的设计问题。...常见问题与易错点过度使用:在不需要的地方使用工厂模式会导致代码复杂度增加,维护成本上升。违反开闭原则:当需要添加新产品时,可能需要修改现有的工厂类,这违反了“对扩展开放,对修改关闭”的原则。...如何避免仅在需要动态选择具体实现或需要解耦创建过程时使用工厂模式。使用抽象工厂模式来进一步封装创建过程,减少对工厂类的修改。...如何避免在产品族之间有明确的关系,并且需要一起创建时使用抽象工厂。确保设计足够灵活,以便在不影响其他部分的情况下添加新的产品族。...C++中更好地管理对象的创建过程,同时保持代码的清晰和可维护性。

    9210

    C++雾中风景6:拷贝构造函数与赋值函数

    初学C++时,这样的结果让我很困惑,所以我们接下来梳理一下这两个函数。 2.拷贝构造函数 上面的代码我们可以看到代码 Line l2 = l1调用了拷贝构造函数。...拷贝构造函数,顾名思义,是一个构造函数,但是它特殊的点就在于在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。...我们知道每个类都会有构造函数,在对象初始化的过程之中,拷贝构造函数提供了一个通过一个同类型的对象对它进行初始化。...C++支持两种初始化形式:拷贝初始化(int a = 5;)和直接初始化(int a(5);)对于其他类型没有什么区别,对于类类型直接初始化直接调用实参匹配的构造函数,拷贝初始化总是调用拷贝构造函数,也就是说...而当对象已经存在,用别的对象来给它进行赋值操作时,调用的就是赋值函数了。 最后的小Tips:一旦在类之中声明了拷贝构造函数与赋值函数,编译器将不会生成缺省的对应函数。

    63120

    Spring如何解决循环依赖的

    1.什么是循环依赖 就是我们有两个服务,A服务,B服务,然后我们在A里注入了B,然后在B里注入了A,这就是循环依赖了,这种情况如果我们不解决的话,那就会出现一个相互依赖注入的死循环。...> singletonObjects = new ConcurrentHashMap(256); /** 二级缓存 保存半成品bean实例,当对象需要被AOP切面代时,保存代理bean的实例...>> singletonFactories = new HashMap(16); 2.2 三级缓存如何解决循环依赖的问题 前置知识:Spring的单例对象的初始化主要分为三步: (1)createBeanInstance...,A会进行提前AOP,所以B中填充的是A的代理对象 当A填充完B时,构成互相循环依赖对方 3.1.4....当A属性填充完后,A和B相互依赖,使得二者都是完整的对象,可见上文3.1.3的图 4.3 不涉及循环依赖的AOP场景 不涉及循环依赖,也就不涉及提前AOP,正常A经过实例化–属性填充–初始化 在初始化时通过

    1K20

    shared_ptr 和 unique_ptr 深入探秘

    C++ 中 shared_ptr 和 unique_ptr 是 C++11 之后被广泛使用的两个智能指针,但是其实他们在使用上还是有一些“秘密”的,我根据平时遇到的两个问题,总结记录一些知识。...C++ 声明和定义最大的区别就是是否发生内存分配,当发生内存分配的时候,必须知道要分配多少内存,通常一个未定义的 struct,未指定长度的数组类型,都会引发 incomplete type 的问题。...所以当 Deleter 非默认时,就不一定需要知道类型的析构函数。...默认构造的时候允许是不完整类型。为什么会这样呢?shared_ptr 怎么处理 Deleter 呢?...Deleter 的类型在 control block 的具体类型上,shared_ptr 本身只持有一个 control block 基类的指针,通过虚函数来调用 Deleter。

    45710

    C++异常

    ✈️C++异常 异常是一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接的调用者处理这个错误。 throw: 当问题出现时,程序会抛出一个异常。...实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获。...只需要在main函数中创建父类对象的引用,当子类中有错误抛出的时候,会返回到main函数的父类,父类指向子类,就完成了一次多态调用,调用子类重写的what()函数。...,否则可能导致对象不完整或没有完全初始化 析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内存泄漏、句柄未关闭等) C++中异常经常会导致资源泄漏的问题,比如在new和delete...构造函数与析构函数内抛异常是一件很严重的事情,如果构造函数有两个指针需要初始化,当第一个指针进行初始化的时候却抛异常了,这个时候就会跳出构造,导致对象创建不完整,除了作用域会调用析构函数,而类构造函数中有一个指针没有初始化

    10110

    精读《Spring 概念》

    模板方法模式:父类先定义一些函数,这些函数之间存在调用关联,将某些设定为抽象函数等待子类继承时去重写。...在实际场景中,两个类相互调用是很常见的,假设现在有 A、B 类相互依赖: @Component public class A { @Autowired private B b;...除了方便之外,IOC 配合 spring 容器概念还可以使获取实例时不用关心一个类实例化需要哪些参数,只需要直接申明获取即可,这样在类的数量特别多,尤其是大量代码不是你写的情况下,不需要阅读类源码也可以轻松获取实例...说到这就提到了 Bean 容器,在 spring 概念中,Bean 容器是对 class 的加强,如果说 Class 定义了类的基本含义,那 Bean 就是对类进行使用拓展,告诉我们应该如何实例化与使用这个类...,* 表示任意返回类型的方法,后面就不用解释了。

    24810

    【C++】一文熟悉C++中的异常机制

    1 C语言传统的异常机制 当程序的某部分检测一个无法处理的问题时,需要用到异常处理,此时检测出问题的部分应该发出某种信号已表明程序遇到了故障,无法继续下去了,给出的信号无序知道故障将在何处解决,一旦发出异常信号...,在C++语言中,异常处理包括: throw: 异常检测部分使用throw表达式来表示程序遇到了无法解决的问题。...它们是以父子类层次结构组织起来的,如下所示: 异常类型 意义描述 std::exception 该异常是所有标准C++异常的父类。 std.::bad_alloc 该异常可以通过 new抛出。...:invalid_argument 当使用了无效的参数时,会抛出该异常。 std:length_error 当创建了太长的 std:string时,会抛出该异常。...:range_error 当尝试存储超出范围的值时,会抛出该异常。 std.:underflow_error 当发生数学下溢时,会抛出该异常。

    16510
    领券