error: member access into incomplete type ‘element_type 编译,类包含和类定义相互包含为成员变量,并且引用成员函数报不是完整类型报错,问题

类的引用

类A定义中 包含了成员变量B,并且调用了类B的成员函数,但是在此之前只是声明了类B,并没有定义类B成员函数的实现,代码如下:

class B;
class A {
private:
    std::shared_ptr<B> m_partB;
public:
    A(std::shared_ptr<B> partB) : m_partB(std::move(partB)) {
        std::cout << "A created with partB" << std::endl;
    }

    void show() const {
        std::cout << "A has partB: " << m_partB->getPartB() << std::endl;
    }
    std::shared_ptr<B> getPartB() const {
        return m_partB;
    }
};

class B {
private:
    std::string m_partB;
public:
    B(std::string partB) : m_partB(std::move(partB)) {
        std::cout << "B created with partB: " << m_partB << std::endl;
    }

    std::string getPartB() const {
        return m_partB;
    }
};

编译报错:

 error: member access into incomplete type 'element_type' (aka 'B')
   90 |         std::cout << "A has partB: " << m_partB->getPartB() << std::endl;
      |                                                ^
statePattern.cpp:80:7: note: forward declaration of 'B'
   80 | class B;
      |       ^
1 error generated.

分析:

编译器报错的原因是 `class B` 的前向声明(`class B;`)无法提供 `B` 的完整定义,而在 `class A` 的 `show` 方法中尝试访问 `m_partB->getPartB()` 时,编译器需要知道 `B` 的完整定义。

编译器工作流程

1. 前向声明的作用


`class B;` 是一个前向声明,告诉编译器 `B` 是一个类,但没有提供它的具体定义。
 前向声明允许在某些情况下使用指针或引用类型,例如:
 

  class B;
  class A {
  private:
      std::shared_ptr<B> m_partB; // 可以声明指针或引用类型
  };


但是,前向声明无法访问 `B` 的成员变量或成员函数,因为编译器不知道 `B` 的具体结构。

2. 编译器解析 `class A`


 在 `class A` 的 `show` 方法中:

void show() const {
      std::cout << "A has partB: " << m_partB->getPartB() << std::endl;
  }


  编译器需要知道 `m_partB` 的类型(`std::shared_ptr<B>`)以及 `B` 的成员函数 `getPartB()`。
 由于 `B` 的定义尚未提供,编译器无法解析 `getPartB()`,因此报错。

3. 编译器报错的具体原因


错误信息类似于:

error: member access into incomplete type 'B'


编译器无法访问 `B` 的成员函数 `getPartB()`,因为 `B` 是一个不完整类型(incomplete type)。
不完整类型意味着编译器知道 `B` 是一个类,但不知道它的具体内容。

解决方法

方法 1:将 `class B` 的完整定义移到 `class A` 之前


将 `class B` 的完整定义放在 `class A` 的定义之前,这样编译器可以解析 `B` 的成员函数。修改后的代码:

#include <memory>
#include <iostream>
#include <string>

// 完整定义类 B
class B {
private:
    std::string m_partB;
public:
    B(std::string partB) : m_partB(std::move(partB)) {
        std::cout << "B created with partB: " << m_partB << std::endl;
    }

    std::string getPartB() const {
        return m_partB;
    }
};

// 定义类 A
class A {
private:
    std::shared_ptr<B> m_partB;
public:
    A(std::shared_ptr<B> partB) : m_partB(std::move(partB)) {
        std::cout << "A created with partB" << std::endl;
    }

    void show() const {
        std::cout << "A has partB: " << m_partB->getPartB() << std::endl;
    }

    std::shared_ptr<B> getPartB() const {
        return m_partB;
    }
};

// 测试代码
int main() {
    auto partB = std::make_shared<B>("Part B Data");
    A a(partB);
    a.show();
    return 0;
}

方法 2:使用 `#include` 引入完整定义


如果 `class B` 的定义在另一个头文件中,可以通过 `#include` 引入 `B` 的完整定义。例如:

// B.hpp
#ifndef B_HPP
#define B_HPP

#include <string>
#include <iostream>

class B {
private:
    std::string m_partB;
public:
    B(std::string partB) : m_partB(std::move(partB)) {
        std::cout << "B created with partB: " << m_partB << std::endl;
    }

    std::string getPartB() const {
        return m_partB;
    }
};

#endif // B_HPP

// A.hpp
#ifndef A_HPP
#define A_HPP

#include "B.hpp"
#include <memory>
#include <iostream>

class A {
private:
    std::shared_ptr<B> m_partB;
public:
    A(std::shared_ptr<B> partB) : m_partB(std::move(partB)) {
        std::cout << "A created with partB" << std::endl;
    }

    void show() const {
        std::cout << "A has partB: " << m_partB->getPartB() << std::endl;
    }

    std::shared_ptr<B> getPartB() const {
        return m_partB;
    }
};

#endif // A_HPP

// main.cpp
#include "A.hpp"

int main() {
    auto partB = std::make_shared<B>("Part B Data");
    A a(partB);
    a.show();
    return 0;
}

编译和运行

编译命令

g++ -std=c++17 -o statePattern main.cpp

运行结果

B created with partB: Part B Data
A created with partB
A has partB: Part B Data

总结

问题原因:`class B` 的前向声明导致编译器无法解析其成员函数。
解决方法
  1. 将 `class B` 的完整定义移到 `class A` 之前。
  2. 或者通过 `#include` 引入 `class B` 的完整定义。
推荐方:方法 2 更加模块化,适合大型项目。

模版解决(可以解决相互包含的问题):

template<typename T>
class A {
private:
    std::shared_ptr<T> m_partB;
public:
    A(std::shared_ptr<T> partB) : m_partB(std::move(partB)) {
        std::cout << "A created with partB" << std::endl;
    }

    void show() const {
        std::cout << "A has partB: " << m_partB->getPartB() << std::endl;
    }
    std::shared_ptr<T> getPartB() const {
        return m_partB;
    }
};

class B {
private:
    std::string m_partB;
public:
    B(std::string partB) : m_partB(std::move(partB)) {
        std::cout << "B created with partB: " << m_partB << std::endl;
    }

    std::string getPartB() const {
        return m_partB;
    }
};

void testTemplateClass() {
    auto partB = std::make_shared<B>("Part B");
    A<B> a(partB);
    a.show();
}
为什么这样可以:

这段代码可以正常运行的原因是编译器能够正确解析模板类 `A` 和普通类 `B` 的定义。以下是详细分析:

编译器如何处理模板类1. 模板类的定义
模板类 `A` 的定义如下:

template<typename T>
class A {
private:
    std::shared_ptr<T> m_partB;
public:
    A(std::shared_ptr<T> partB) : m_partB(std::move(partB)) {
        std::cout << "A created with partB" << std::endl;
    }

    void show() const {
        std::cout << "A has partB: " << m_partB->getPartB() << std::endl;
    }

    std::shared_ptr<T> getPartB() const {
        return m_partB;
    }
};

模板类 `A` 的定义是通用的,编译器不会立即实例化模板类。
模板类的实例化发生在使用模板类时,例如 `A<B>`。


2. 普通类 `B` 的定义
普通类 `B` 的定义如下:
 

class B {
private:
    std::string m_partB;
public:
    B(std::string partB) : m_partB(std::move(partB)) {
        std::cout << "B created with partB: " << m_partB << std::endl;
    }

    std::string getPartB() const {
        return m_partB;
    }
};

类 `B` 的定义是完整的,编译器可以解析其成员变量 `m_partB` 和成员函数 `getPartB()`。

3. 模板类实例化
在以下代码中:

auto partB = std::make_shared<B>("Part B");
A<B> a(partB);
a.show();

`A<B>` 是模板类 `A` 的实例化,编译器会将 `T` 替换为 `B`。
编译器会检查 `B` 的定义是否完整,以确保可以访问 `B` 的成员函数 `getPartB()`。

编译器的工作流程

1. 解析普通类 `B` 的定义:
    编译器首先解析 `B` 的定义,确保 `B` 的成员变量和成员函数可以被访问。

2. 解析模板类 `A` 的定义:
  编译器解析模板类 `A` 的定义,但不会立即实例化模板类。

3. 实例化模板类 `A<B>`:
   - 在 `A<B> a(partB)` 处,编译器实例化模板类 `A`,将 `T` 替换为 `B`。
   - 编译器检查 `B` 的定义是否完整,以确保可以访问 `m_partB->getPartB()`。

4. 生成代码:
 编译器生成 `A<B>` 的代码,并链接到程序中。

总结

这段代码可以正常运行的原因是:

1. 模板类 `A` 的定义是通用的:
   编译器在实例化模板类时,会替换模板参数 `T` 为具体类型(如 `B`)。

2. 普通类 `B` 的定义是完整的:
   编译器可以解析 `B` 的成员变量和成员函数。

3. 模板类实例化发生在使用时:
 编译器在 `A<B>` 的实例化过程中,确保 `B` 的定义是完整的。

这段代码的设计符合 C++ 的模板机制,因此可以正常编译和运行。

相互包含,采用头文件也不行,只能采用模版编程:

#include <memory>
#include <iostream>
template<typename TrafficLightContext>
class TrafficLightState{
public:
    virtual void handle(TrafficLightContext& context)=0;
    virtual ~TrafficLightState()=default;
};
template<typename TrafficLightContext>
class GreenState;

template<typename TrafficLightContext>
class RedState:public TrafficLightState<TrafficLightContext>{
public: 
    void handle(TrafficLightContext& context)override{
        std::cout<<"set red Light still set Green state\n";
        context.setState(std::move(std::make_unique<GreenState<TrafficLightContext>>()));    
    }

};

template<typename TrafficLightContext>
class YellowState:public TrafficLightState<TrafficLightContext>{
public: 
    void handle(TrafficLightContext& context)override{
        std::cout<<"set Yellow Light 3 seconds and set Red state\n";
        context.setState(std::move(std::make_unique<RedState<TrafficLightContext>>()));    
    }

};

template<typename TrafficLightContext>
class GreenState:public TrafficLightState<TrafficLightContext>{
public: 
    void handle(TrafficLightContext& context)override{
        std::cout<<"set red Green 40 seconds and set Yellow state\n";
        context.setState(std::move(std::make_unique<YellowState<TrafficLightContext>>()));    
    }

};


template<typename TrafficLightContext>
using lightStatePtr = std::unique_ptr<TrafficLightState<TrafficLightContext>>;
class TrafficLight{
private:
    lightStatePtr<TrafficLight> m_ptr;
public:
    TrafficLight(lightStatePtr<TrafficLight> initstate)
    :m_ptr(std::move(initstate)){}

    void setState(lightStatePtr<TrafficLight> newState){
        m_ptr = std::move(newState);
    }

    void handle(){
        if(m_ptr){
            m_ptr->handle(*this);
        }
    }
};

void testStatePattern(){
    auto trafficLight = TrafficLight(std::make_unique<RedState<TrafficLight>>());

    for(int i = 0;i<3;i++){
        trafficLight.handle();
        trafficLight.handle();
        trafficLight.handle();
    }
}


int main(){
    testStatePattern();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值