cpp只声明不定义的情况

⌚Time: 2021-11-27 11:04:31

👨‍💻Author: Jack Ge

回答来自AI

在C++中,声明但不定义是常见的模式,以下是主要应用场景:

1. 前置声明(Forward Declaration)

最常见的情况,用于解决循环依赖或减少编译依赖:

// 类前置声明
class MyClass;          // 只声明,不定义
class AnotherClass*;    // 类指针前置声明

// 函数前置声明
void process(MyClass* obj);  // 函数声明,定义在其他文件

// 模板前置声明
template<typename T>
class MyTemplate;

// 使用场景
class B;  // 前置声明

class A {
    B* b_ptr;  // 使用指针或引用,不需要完整定义
};

class B {
    A* a_ptr;
};

2. 接口/抽象类中的纯虚函数

class Interface {
public:
    virtual void doSomething() = 0;  // 纯虚函数,只有声明
    virtual ~Interface() = default;  // 虚析构函数可以有定义
};

3. extern变量声明

// header.h
extern int global_var;      // 声明,定义在.cpp文件中
extern const int MAX_SIZE;  // 常量声明

// source.cpp
int global_var = 42;        // 实际定义
const int MAX_SIZE = 100;

4. 不完整类型(Incomplete Type)

class OpaqueType;  // 不完整类型声明

class Container {
    OpaqueType* ptr;      // 只能使用指针或引用
    // OpaqueType obj;    // 错误:不完整类型不能实例化
};

// 后续可能在另一个翻译单元中定义
// impl.cpp
class OpaqueType {
    int data;
    void method();
};

5. 函数模板和类模板声明

// 模板声明
template<typename T>
void templateFunc(T param);  // 声明,可能在其他地方特化/定义

template<typename T>
class TemplateClass;         // 模板类声明

6. 友元声明

class Secret {
private:
    int hidden;
    friend void peek(const Secret&);  // 友元函数声明
};

// 友元函数可能在别处定义
void peek(const Secret& s) {
    std::cout << s.hidden << std::endl;
}

7. 避免多重定义

// inline函数/变量在头文件中定义
// 非inline函数在头文件中声明,在.cpp中定义

// header.h
inline int add(int a, int b) { return a + b; }  // 可以多次定义
int multiply(int a, int b);                     // 只能声明

// source.cpp
int multiply(int a, int b) { return a * b; }    // 实际定义

8. 编译器特定用途

// 阻止默认函数生成
class NonCopyable {
public:
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;      // 声明为delete
    NonCopyable& operator=(const NonCopyable&) = delete;
};

// 显式默认函数
class Defaulted {
public:
    Defaulted() = default;                         // 声明为default
};

9. 特定设计模式

Pimpl惯用法(Pointer to Implementation)

// widget.h
class Widget {
public:
    Widget();
    ~Widget();
    void doWork();
private:
    class Impl;          // 只声明,不暴露实现
    std::unique_ptr<Impl> pImpl;
};

// widget.cpp
class Widget::Impl {     // 在.cpp中定义
    int data;
    void helper();
public:
    void doWorkImpl();
};

Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;
void Widget::doWork() { pImpl->doWorkImpl(); }

10. 链接规范

// C链接声明
extern "C" {
    int c_function(int arg);  // 声明C函数,可能在C库中定义
}

// 动态库导出
#ifdef MYLIB_EXPORTS
    #define MYLIB_API __declspec(dllexport)
#else
    #define MYLIB_API __declspec(dllimport)
#endif

MYLIB_API void exported_function();  // 声明,定义在DLL中

11. 概念约束(C++20起)

template<typename T>
concept Addable = requires(T a, T b) {
    { a + b } -> std::same_as<T>;  // 概念约束,不是定义
};

常见错误和注意事项

// 1. ODR违规(One Definition Rule)
// header.h
void helper() { /* 实现 */ }  // 每个包含此头文件的翻译单元都会定义它
// 应该改为inline或只在.cpp中定义

// 2. 未定义引用错误
extern int undefined_var;  // 声明了但未定义
// 使用时会链接错误

// 3. 不完整类型误用
class Incomplete;
Incomplete create();  // 返回不完整类型 - 错误!
void use(Incomplete); // 参数传递不完整类型 - 错误!

最佳实践

  1. 头文件只放声明,定义放在.cpp文件
  2. 使用前置声明减少编译依赖
  3. Pimpl模式隐藏实现细节
  4. 检查ODR规则,避免多重定义
  5. 确保声明与定义匹配(签名、链接规范等)

这种声明不定义的模式是C++模块化设计和分离接口与实现的基础。