c++重载操作符研究之 operator ->()

本文探讨了在C++中重载操作符->的方法,并通过具体代码示例展示了不同模板类A1、A2、A3及B1如何实现这一功能。同时对比了递归推导的可行性和限制。

本章是介绍重载操作符 -> 的研究,上代码先

 

#include "stdafx.h"
#include <iostream>
#include <string>


template<typename T>
class A1 {
public:
    A1(T* t) : m_imp(t) {
        std::cout << "A1()" << std::endl;
    }
    ~A1() {
        std::cout << __FUNCTION__ << std::endl;
    }

    T* operator ->() {
        std::cout << __FUNCTION__ << std::endl;
        return m_imp;
    }
private:
    T* m_imp;
};

template<typename T>
class A2 {
public:
    A2(T* t) : m_imp(t) {}

    A1<T> operator ->() {
        std::cout << __FUNCTION__ << std::endl;
        A1<T> a(m_imp);
        return a;
    }
private:
    T* m_imp;
};

template<typename T>
class A3 {
public:
    A3(T* t) : m_imp(t) {}

    A2<T> operator ->() {
        std::cout << __FUNCTION__ << std::endl;
        A2<T> a(m_imp);
        return a;
    }
private:
    T* m_imp;
};

class Test {
public:
    void show() {
        std::cout << m_a << std::endl;
    }
private:
    int m_a;
};

template<typename T>
class B1 {
public:
    B1(T* t) : m_imp(t) {
        std::cout << __FUNCTION__ << std::endl;
    }
    B1(T& t) : m_imp(&t) {
        std::cout << __FUNCTION__ << std::endl;
    }
    T* operator->() {
        std::cout << __FUNCTION__ << std::endl;
        return m_imp;
    }
private:
    T* m_imp;
};

int _tmain(int argc, _TCHAR* argv[])
{
    Test* pt = new Test();

    A1<Test> a1(pt);
    a1->show();
    a1.A1::operator ->()->show();

    A2<Test> a2(pt);
    a2->show();
    // 可自动推导出如下式
    // a2.A2::operator ->().A1::operator ->()->show();

    A3<Test> a3(pt);
    a3->show();
    // 可自动推导出如下式
    // a3.A3<Test>::operator ->().A2::operator ->().A1::operator ->()->show();

    B1<Test> b1(pt);
    b1->show();

    B1<B1<Test> > b2(b1);
    b2->operator ->()->show();
    b2.B1<B1<Test> >::operator ->()->B1<Test>::operator ->()->show();
    // b2->show(); 错误,不能递归推导

	return 0;
}


基于以上代码,可以看出 对->运算符的重载,编译器无法进行递归推导.

这种递归推导,编译器可以在编译期处理,为什么编译器不处理这种递归推导,留待思考

以上代码在vc编译器测试通过。

### 关于 `->` 操作符访问非重载类成员时的解决方案 在 C++ 中,当尝试通过自定义重载的 `->` 操作符来访问一个未被正确映射到目标类型的成员时,可能会引发编译错误。这是因为 `->` 的行为完全依赖于其重载函数所返回的对象类型及其支持的操作。 #### 错误原因分析 如果 `operator->()` 返回了一个不兼容的目标对象(例如返回了某种代理对象而非实际期望的原始类型),则后续对该对象成员的访问可能无法解析成功[^1]。具体而言,在链式调用场景下(如嵌套多个 `operator->()` 实现的情况),最终返回的结果需能够直接支持所需的方法或属性访问;否则会触发编译器报错提示找不到对应成员。 针对上述情况下的解决办法如下: --- #### 方法一:调整 `operator->()` 的返回值类型以匹配预期接口需求 确保每次重载后的 `operator->()` 函数都返回指向适当数据结构的指针形式,使得之后可以正常继续调用这些数据上的公有成员函数或变量。比如原例子中最后一步应改为让顶层封装类提供对底层存储字符串集合大小查询能力的支持: ```cpp class A { public: string* operator->() const { return &data[curr]; } private: vector<string> data{"str1", "str2", "str3"}; mutable size_t curr = 0; }; class B { public: A* operator->() { return &a; } private: A a; }; class C { public: B* operator->() { return &b; } // 添加额外辅助功能以便获取内部状态信息 size_t size() const { return b.a.data.size(); } private: B b; }; ``` 这样修改后, 主程序里可以直接利用新增加的公开方法获得容器长度而不必再试图非法越界读取不存在字段:`cout << c.size() << endl;`. --- #### 方法二:引入中间层适配逻辑实现间接寻址机制 另一种方式是在某些特殊情况下考虑设计专门用于桥接不同层次间差异性的过渡组件作为中介角色存在。它既保留原有复杂度又不会破坏现有架构稳定性的同时还允许灵活扩展新的特性进来满足特定业务诉求. 例如我们可以创建一个新的包装类别用来处理这种情况: ```cpp template<typename T> struct SmartPtrAdapter{ explicit SmartPtrAdapter(T& obj):ref(obj){} decltype(auto) operator->(){ return &(ref.*T::member); // 假设知道确切名称叫 member } private: T& ref; }; // 使用示例 int main(){ C c; auto adapter=SmartPtrAdapter(c); cout<<adapter->size()<<endl;// 正确工作 } ``` 这里我们假设已经提前获知了想要操作的具体子项叫做 `size`, 并将其硬编码进了模板参数列表当中去. 这种做法虽然牺牲了一定程度上通用性但是却极大简化了解决问题过程并保持清晰直观易于维护的优点. --- #### 总结说明 无论是采用哪种策略都需要仔细权衡利弊得失以及考虑到未来可能出现的变化趋势做出合理判断选择最适合自己项目实际情况的最佳实践路径前进方向才行啊!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值