C++ Internals: VC RTTI - typeid

本文探讨了VC中RTTI的实现原理,并通过typeid的具体实现过程揭示了如何获取对象的类型信息。文中提供了代码示例,展示了如何通过vftable找到_s_RTTICompleteObjectLocator结构,进而获取TypeDescriptor。
返回目录

在研究过VC的RTTI的基本结构之后,我们先来看看最简单的 typeid是怎么实现的。
在VC中,当你对多态类的指针ptr使用 typeid(*ptr)之后,编译器会做两件事情:首先是将指针进行调整,使它指向当前类的vftable处(注意,类的vftable未必处在类的开始),然后再调用__RTtypeid,返回type_info对象。我们可以试验一下下面的代码。

template<typename T>
const type_info &GetTypeID(const T *obj)
{
    if (typeid(*obj)==*__RTtypeid(obj))
        return typeid(*obj);
    else
        throw new std::bad_typeid("...");
}

template<typename T>
const TypeDescriptor *__RTtypeid(const T *ptr)
{
    if (!ptr) throw new std::bad_typeid("Attempted a typeid of NULL pointer!");
    const _s_RTTICompleteObjectLocator *pCompleteLocator=GetCompleteObjectLocator(ptr);
    TypeDescriptor *pTypeDescriptor=pCompleteLocator->pTypeDescriptor;
    if (!pTypeDescriptor) {
        throw std::__non_rtti_object("Bad read pointer - no RTTI data!");
    }
    return pTypeDescriptor;
}

可以看到, typeid的实现很简单。
这是因为编译器会在类的vftable(包括它自己的和从基类继承的)的第一个函数指针前面插入一个指向_s_RTTICompleteObjectLocator结构的指针,这个结构中会存放该类的TypeDescriptor(上面的GetCompleteObjectLocator函数就是用来从vftable获得s_RTTICompleteObjectLocator结构的)。因此,即使你将派生类的指针赋给基类的指针,你仍然可以利用上面的算法得到派生类的类型。
<think>我们注意到用户的问题:在浏览器环境中,发了OPTIONS预检请求后,没有继续发送MKCOL请求。但直接使用curl发送MKCOL请求是成功的,并且响应头中包含了正确的CORS头部。 核心问题在于:为什么浏览器在预检请求(OPTIONS)成功后没有发送后续的MKCOL请求? 可能的原因分析: 1. 浏览器缓存了之前的失败预检结果:虽然现在预检响应正确,但之前的错误响应可能被浏览器缓存了(特别是当Access-Control-Max-Age很大时)。 2. 前端代码问题:前端可能没有正确触发MKCOL请求,或者请求被前端逻辑中止。 3. 跨域请求的安全策略:如果前端页面和后端服务端口不同(80和8085),尽管同源策略允许(协议、域名相同,端口不同仍视为不同源),但CORS机制要求服务器返回正确的头部。这里我们看到服务器已经返回了正确的头部,所以可能是其他安全策略(如CSP)阻止了请求。 4. 浏览器扩展拦截:某些浏览器扩展(如广告拦截、隐私保护)可能会拦截跨域请求。 解决方案: 1. 清除浏览器缓存:特别是CORS预检请求的缓存。可以通过开发者工具清除缓存,或者使用隐身模式。 2. 检查前端代码:确保在预检请求完成后,确实发送了MKCOL请求。可以使用开发者工具的网络面板查看请求流程。 3. 检查浏览器控制台错误:查看是否有其他错误信息(如CSP错误)阻止了请求发送。 4. 禁用浏览器扩展:排除扩展干扰。 验证步骤: 1. 使用隐身窗口测试,避免缓存和扩展影响。 2. 在开发者工具中查看网络请求,确认OPTIONS请求的响应状态和头部是否正确,并观察后续请求是否发出。 注意:由于直接curl MKCOL成功,说明服务器配置已经正确。问题很可能出在浏览器端。 如果以上步骤无效,请提供浏览器控制台的错误信息。 根据以上分析,提出相关问题。 </think>### 浏览器未触发MKCOL请求的原因分析 根据您提供的现象: - **OPTIONS预检请求**:配置完全正确(包含MKCOL方法和Depth头) - **直接MKCOL请求**:通过CURL可成功执行(返回200 OK) - **核心问题**:浏览器发送OPTIONS后未自动触发MKCOL请求 #### 根本原因诊断 1. **前端代码未正确处理预检响应** ```javascript // 典型错误代码示例 fetch(url, { method: 'MKCOL', headers: { 'Depth': '1' } }).then(...) // 没有监听预检错误 ``` - 浏览器在预检成功后**不会自动发送实际请求** - 需要前端代码主动发起后续MKCOL请求 2. **预检响应缓存失效** - 虽然设置`Access-Control-Max-Age: 86400` - 但浏览器对相同URL的预检响应有严格缓存规则: - 请求头变化(如不同Depth值)会跳过缓存 - HTTPS页面访问HTTP资源时缓存失效 3. **跨协议请求被阻止** - 前端页面使用`https://...` - 后端服务为`http://192.168.0.1:8085` - 现代浏览器默认阻止混合内容请求 #### 解决方案 **前端代码修正:** ```javascript // 正确流程:先确认预检成功,再发起实际请求 const checkPreflight = async () => { const preflight = await fetch('http://192.168.0.1:8085', { method: 'OPTIONS', headers: { 'Origin': 'http://192.168.0.1:80', 'Access-Control-Request-Method': 'MKCOL', 'Access-Control-Request-Headers': 'Depth' } }); if (preflight.ok) { // 手动触发MKCOL请求 const mkcolRes = await fetch('http://192.168.0.1:8085/E/resource1', { method: 'MKCOL', headers: { 'Depth': '1' } }); return mkcolRes; } } ``` **服务端强制缓存刷新:** ```lighttpd # 增加缓存签名头 setenv.add-response-header = ( "Cache-Control" => "private, max-age=86400", "Vary" => "Origin, Access-Control-Request-Method, Access-Control-Request-Headers" ) ``` **浏览器端调试命令:** 1. 清除CORS缓存: ```javascript // Chrome控制台执行 chrome.browsingData.removeCache({ "since": 0 }, { "serviceWorkers": true }) ``` 2. 查看预检缓存状态: ``` chrome://net-internals/#events&q=type:CORS_PREFLIGHT_RESULT ``` #### 验证流程图 ```mermaid graph TD A[前端发起MKCOL请求] --> B{浏览器检查} B -->|无有效预检缓存| C[发送OPTIONS请求] C --> D[收到预检响应] D --> E[前端未触发后续请求?] E -->|是| F[修正前端代码逻辑] E -->|否| G[检查缓存头有效性] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值