首页
学习
活动
专区
工具
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结果可能为空并进行检查。

92610
  • 先有Class还是先有Object?

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

    21620

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

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

    36610

    c++两个互相引用问题

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

    1.3K20

    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++new和delete详解

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

    1.1K50

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

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

    78120

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

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

    1.1K40

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

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

    8610

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

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

    8610

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

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

    60820

    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。

    38610

    精读《Spring 概念》

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

    23810

    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 发生数学下溢,会抛出该异常。

    11410

    轻松搞定面试中“虚”

    当然,并不是要把所有析构函数都写成虚函数。因为里面有虚函数时候,编译器会给添加一个虚函数表,里面来存放虚函数指针,这样就会增加存储空间。...虚函数意思就是开启动态绑定,程序会根据对象动态类型来选择要调用方法。然而在构造函数运行时候,这个对象动态类型不完整,没有办法确定它到底是什么类型,故构造函数不能动态绑定。...虚拟继承与普通继承不同是,虚拟继承可以防止出现diamond继承,一个派生中同时出现了两个子对象。也就是说,为了保证这一点,在虚拟继承情况下,基子对象布局是不同于普通继承。...c++通过下面两个操作符提供RTTI: (1)typeid:返回指针或引用所指对象实际类型。 (2)dynamic_cast:将基类型指针或引用安全转换为派生类型指针或引用。...对于带虚函数,在运行时执行RTTI操作符,返回动态类型信息;对于其他类型,在编译执行RTTI,返回静态类型信息。 参考 众网友博客

    66520

    C++】异常机制

    二、C++异常概念 异常是一种处理错误方式,一个函数发现自己无法处理错误时就可以抛出异常,让函数直接或间接调用者处理这个错误。 throw: 问题出现时,程序会抛出一个异常。...实际中抛出和捕获匹配原则有个例外,并不都是类型完全匹配,可以抛出派生对象,使用基捕获,这个在实际中非常实用,我们后面会详细介绍这个。...Func 函数内部发生了除0错误时候,array 资源没有得到释放,会发生内存泄漏,如下图: 那么该如何解决呢?...而C++异常机制,调用链很深时候,直接跳到处理错误地方,不用层层返回。...C++异常缺点 异常会导致程序执行流乱跳,并且非常混乱,并且是运行时出错抛异常就会乱跳。这会导致我们跟踪调试以及分析程序时,比较困难。 异常会有一些性能开销。

    8710

    【Java】已解决:java.lang.ClassCircularityError

    在Java开发过程中,java.lang.ClassCircularityError是一种相对少见但极具破坏性错误。开发者在处理复杂依赖关系,可能会偶然遇到这个错误。...本文将分析该错误背景、可能原因、错误代码示例及其解决方法,帮助开发者理解并解决这一问题。...JVM发现两个或多个相互依赖,形成循环依赖关系,就会抛出这个错误。...这种错误通常发生在以下场景: 两个或多个互相引用,导致加载器在尝试加载一个,不得不先加载另一个,而另一个又依赖于前一个。...和B相互依赖,导致在初始化A需要加载B,而加载B又需要加载A,形成循环。

    8610
    领券