类的引用
类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;
}