VC:__declspec(novtable)

C++ vtable 优化
标题  VC:__declspec(novtable)     选择自 toby 的 Blog
关键字  VC:__declspec(novtable)
出处 
 C++里virtual的缺陷就是vtable会增大代码的尺寸,看vcl时,object pascal里virtual也有vtable的问题,于是又了dynamic,两种方法各有利弊。但是在C++里却没有这样的机制,原来也没深想过,今天看MFC代码时,在CObject的定义时:
class AFX_NOVTABLE CObject
{
...
}

AFX_NOVTABLE是什么东东?是个宏,在Afxver_.h中:
#if _MSC_VER >= 1100 && !defined(_DEBUG)
#define AFX_NOVTABLE __declspec(novtable)
#else
#define AFX_NOVTABLE
#endif

也就是说在你编译Release版本时,在CObject前是__declspec(novtable),在debug版本没有这个限制。MSDN里的解释是:
-----------------------------------------------------------------

Microsoft Specific

This is a __declspec extended attribute.

This form of __declspec can be applied to any class declaration, but should only be applied to pure interface classes, that is, classes that will never be instantiated on their own. The __declspec stops the compiler from generating code to initialize the vfptr in the constructor(s) and destructor of the class. In many cases, this removes the only references to the vtable that are associated with the class and, thus, the linker will remove it. Using this form of __declspec can result in a significant reduction in code size.

If you attempt to instantiate a class marked with novtable and then access a class member, you will receive an access violation (AV).

Example
// novtable.cpp
#include <stdio.h>
class __declspec(novtable) X
{
public:
   virtual void mf();
};

class Y : public X
{
public:
   void mf()
   {
      printf("In Y/n");
   }
};

int main()
{
   // X *pX = new X();
   // pX->mf();   // AV at runtime
   Y *pY = new Y();
   pY->mf();
}
Output
In Y

END Microsoft Specific
-----------------------------------------------------------------
依照AFX_NOVTABLE的声明,对CObject在debug模式,是不起作用的,而在release模式时将移除CObject的vtable,这是release比debug版本的尺寸小的原因之一吧。

<think> </think> ### 解决"Undefined symbol"链接错误的步骤: #### 1. **确认实现文件是否包含** - 检查`.cpp`文件中是否存在方法实现: ```cpp // TSFileManager.cpp #include "TSFileManager.h" void TSFileManager::SetStrCacheFilePrefix(const std::string& strPrefix) { strCacheFilePrefix = strPrefix; } ``` - 📌 **关键验证**: ```bash # 检查目标文件是否包含符号(Unix) nm -C TSFileManager.o | grep SetStrCacheFilePrefix ``` 预期输出应包含`T`(已定义文本符号)而非`U`(未定义) #### 2. **检查构建系统配置** - **CMake**:确保源文件在`add_library`/`add_executable`中列出 ```cmake add_library(FileManager STATIC TSFileManager.cpp # 必须包含实现文件 OtherFiles.cpp ) ``` - **Makefile**:确认编译规则 ```makefile TSFileManager.o: TSFileManager.cpp TSFileManager.h g++ -c $< -o $@ ``` #### 3. **解决命名空间冲突** - 若类在命名空间内,实现需显式限定: ```cpp // 头文件声明 namespace Storage { class TSFileManager { ... }; } // 实现文件 void Storage::TSFileManager::SetStrCacheFilePrefix(...) {} // 正确 void TSFileManager::SetStrCacheFilePrefix(...) {} // 错误! ``` #### 4. **处理标准库ABI兼容性问题** - **问题特征**:符号包含`std::__1`(libc++)或`std::__cxx11`(libstdc++) - **统一方案**: ```bash # 方案1:全部使用libc++(Clang默认) clang++ -stdlib=libc++ *.cpp -o app # 方案2:全部使用libstdc++(GCC默认) g++ -D_GLIBCXX_USE_CXX11_ABI=1 *.cpp -o app ``` #### 5. **修复动态库符号导出问题** - **Windows**: ```cpp class __declspec(dllexport) TSFileManager { ... }; // 导出整个类 // 或单独导出方法 __declspec(dllexport) void SetStrCacheFilePrefix(...); ``` - **Linux/macOS**: ```cpp __attribute__((visibility("default"))) void TSFileManager::SetStrCacheFilePrefix(...) {...} ``` ### 快速诊断流程图: ```mermaid graph TD A[出现Undefined symbol错误] --> B{实现文件是否编译?} B -->|否| C[添加.cpp文件到构建系统] B -->|是| D{符号在.o文件中?} D -->|否| E[检查.cpp文件内容/路径] D -->|是| F{是否ABI兼容?} F -->|是| G[统一标准库版本] F -->|否| H[检查命名空间/类限定符] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值