C++ 类成员访问运算符 -> 重载

本文深入探讨了C++中类成员访问运算符重载的概念及其应用,包括指针行为、智能指针功能和间接引用运算符的使用。通过实例展示了如何实现重载运算符以增强类的功能。


类成员访问运算符( -> )可以被重载,但它较为麻烦。它被定义用于为一个类赋予"指针"行为。运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。

运算符 -> 通常与指针引用运算符 * 结合使用,用于实现"智能指针"的功能。这些指针是行为与正常指针相似的对象,唯一不同的是,当您通过指针访问对象时,它们会执行其他的任务。比如,当指针销毁时,或者当指针指向另一个对象时,会自动删除对象。

间接引用运算符 -> 可被定义为一个一元后缀运算符。也就是说,给出一个类:

class Ptr{
   //...
   X * operator->();
};

类 Ptr 的对象可用于访问类 X 的成员,使用方式与指针的用法十分相似。例如:

void f(Ptr p )
{
   p->m = 10 ; // (p.operator->())->m = 10
}

语句 p->m 被解释为 (p.operator->())->m。同样地,下面的实例演示了如何重载类成员访问运算符 ->。

#include <iostream>
#include <vector>
using namespace std;

// 假设一个实际的类
class Obj {
   static int i, j;
public:
   void f() const { cout << i++ << endl; }
   void g() const { cout << j++ << endl; }
};

// 静态成员定义
int Obj::i = 10;
int Obj::j = 12;

// 为上面的类实现一个容器
class ObjContainer {
   vector<Obj*> a;
public:
   void add(Obj* obj)
   { 
      a.push_back(obj);  // 调用向量的标准方法
   }
   friend class SmartPointer;
};

// 实现智能指针,用于访问类 Obj 的成员
class SmartPointer {
   ObjContainer oc;
   int index;
public:
   SmartPointer(ObjContainer& objc)
   { 
       oc = objc;
       index = 0;
   }
   // 返回值表示列表结束
   bool operator++() // 前缀版本
   { 
     if(index >= oc.a.size()) return false;
     if(oc.a[++index] == 0) return false;
     return true;
   }
   bool operator++(int) // 后缀版本
   { 
      return operator++();
   }
   // 重载运算符 ->
   Obj* operator->() const 
   {
     if(!oc.a[index])
     {
        cout << "Zero value";
        return (Obj*)0;
     }
     return oc.a[index];
   }
};

int main() {
   const int sz = 10;
   Obj o[sz];
   ObjContainer oc;
   for(int i = 0; i < sz; i++)
   {
       oc.add(&o[i]);
   }
   SmartPointer sp(oc); // 创建一个迭代器
   do {
      sp->f(); // 智能指针调用
      sp->g();
   } while(sp++);
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

10
12
11
13
12
14
13
15
14
16
15
17
16
18
17
19
18
20
19
21

C++ 重载运算符和重载函数 C++ 重载运算符和重载函数



FROM: http://www.runoob.com/cplusplus/class-member-access-operator-overloading.html

<think> 我们被要求回答关于C++运算符 -> 或 ->* 应用于非指针类型的问题及解决办法。 根据引用资料,用户提供的引用主要讨论了指针的基本操作(*和&),以及成员指针运算符->)的简单示例(引用[5]提到了成员指针运算符,但仅展示了通过指针访问结构体成员)。 但是,用户的问题是关于“-> 或 ->* 应用于非指针类型”的问题及解决方案。这意味着我们需要解释当这些运算符被用于非指针类型时会发生什么,以及如何解决这类问题。 首先,我们需要明确: 1. `->` 运算符:通常用于指针访问对象的成员。例如,如果有一个指向对象的指针ptr,则可以使用ptr->member来访问成员。 2. `->*` 运算符:用于通过指向成员的指针来访问对象的成员。例如,如果有一个指向类成员的指针ptr_mem,则可以使用obj->*ptr_mem(如果obj是指针)或obj.*ptr_mem(如果obj是对象)来访问。 问题:当这些运算符应用于非指针类型时,编译器会报错。 常见错误场景: 1. 对一个非指针类型的变量使用`->`运算符。 例如:有一个对象(不是指针)obj,却试图使用obj->member。 2. 对一个非指针类型的变量使用`->*`运算符。 解决方案: 1. 对于`->`运算符:确保左侧操作数是一个指针类型。如果是一个对象实例,应该使用`.`运算符。 错误示例: MyClass obj; obj->member; // 错误:obj不是指针 正确做法: MyClass obj; obj.member; // 使用点运算符 或者,如果确实需要指针,则应该使用指针: MyClass *ptr = &obj; ptr->member; 2. 对于`->*`运算符:同样,左侧操作数必须是指针。如果是一个对象,应该使用`.*`运算符。 错误示例: MyClass obj; int MyClass::*ptr_mem = &MyClass::member; // 指向成员的指针 obj->*ptr_mem; // 错误:obj不是指针 正确做法: obj.*ptr_mem; // 使用.*运算符 // 或者使用指针 MyClass *ptr = &obj; ptr->*ptr_mem; 另外,需要注意的是,`->*`运算符的左侧操作数可以是指向对象的指针,而右侧操作数是指向该对象成员的指针(包括数据成员和成员函数)。 总结: 运算符的使用必须与操作数的类型匹配: - 对于对象实例(非指针),使用`.`和`.*`。 - 对于对象指针,使用`->`和`->*`。 因此,当出现将`->`或`->*`应用于非指针类型时,解决方案就是检查左侧操作数的类型,如果是对象实例则改为使用`.`或`.*`;如果确实需要用指针访问,则先将对象地址赋给指针变量,再通过指针访问。 下面我们用一个代码示例来说明: ```cpp #include <iostream> using namespace std; class MyClass { public: int member; void print() { cout << "member = " << member << endl; } }; int main() { MyClass obj; // 错误用法: // obj->member = 10; // 错误:'obj'不是指针 // 正确用法(使用点运算符): obj.member = 10; obj.print(); // 使用指针 MyClass *ptr = &obj; ptr->member = 20; ptr->print(); // 指向成员的指针 int MyClass::*p_member = &MyClass::member; // 错误用法: // obj->*p_member = 30; // 错误:'obj'不是指针 // 正确用法(使用.*运算符): obj.*p_member = 30; obj.print(); // 正确用法(使用->*运算符): ptr->*p_member = 40; ptr->print(); return 0; } ``` 在上面的代码中,我们展示了错误用法(注释掉的部分)和正确用法。 如果用户遇到此类错误,编译器通常会给出明确的错误信息,例如“left operand has type 'MyClass' but requires pointer type”。根据错误信息,用户应该检查左侧操作数的类型。 因此,解决办法就是根据左侧操作数的类型选择正确的运算符- 对象:使用`.`和`.*` - 指针:使用`->`和`->*` 这样就能解决运算符应用于非指针类型的问题。 </think>### C++运算符 `->` 或 `->*` 应用于非指针类型的问题及解决方案 在 C++ 中,`->` 和 `->*` 是专门用于**指针类型**的运算符。当它们被错误地应用于非指针类型时,会导致编译错误。以下是详细分析和解决方案: --- ### 问题原因分析 1. **运算符用途**: - `->`:通过指针访问对象的成员(等价于 `(*ptr).member`) - `->*`:通过指针访问成员指针指向的成员(等价于 `(*ptr).*member_ptr`) 2. **错误场景**: ```cpp struct Student { int age; }; Student s; s->age; // 错误:s 是对象而非指针 ``` 编译器报错:`error: base operand of '->' has non-pointer type 'Student'` --- ### 解决方案 #### 方法 1:正确使用运算符类型 - **对象实例**用 `.` 或 `.*` - **对象指针**用 `->` 或 `->*` ```cpp Student s; Student* ptr = &s; // 正确声明指针 // 访问成员 s.age = 18; // 对象用 . ptr->age = 20; // 指针用 -> // 成员指针访问 int Student::*age_ptr = &Student::age; s.*age_ptr = 22; // 对象用 .* ptr->*age_ptr = 25; // 指针用 ->* ``` #### 方法 2:重载运算符(高级用法) 若需自定义类型支持 `->`,可重载 `operator->()`: ```cpp class Wrapper { Student* ptr; public: explicit Wrapper(Student* p) : ptr(p) {} Student* operator->() { return ptr; } // 重载 -> }; Wrapper w(&s); w->age = 30; // 合法:w.operator->() 返回指针 ``` #### 方法 3:智能指针的自动转换 使用 `std::unique_ptr`/`std::shared_ptr` 自动支持 `->`: ```cpp auto ptr = std::make_unique<Student>(); ptr->age = 35; // 智能指针隐式支持 -> ``` --- ### 常见错误案例及修复 | **错误代码** | **错误原因** | **修复代码** | |----------------------|---------------------------|---------------------| | `Student s; s->age;` | 对象误用 `->` | `s.age;` | | `int (Student::*mem_ptr) = &Student::age; s->*mem_ptr;` | 对象误用 `->*` | `s.*mem_ptr;` | | `Student* p = nullptr; p->age;` | 空指针解引用(运行时崩溃) | 检查 `if (p) p->age;` | --- ### 关键总结 1. **类型匹配**: ```mermaid graph LR A[操作数类型] --> B[对象实例] A --> C[对象指针] B --> D[使用 . 或 .*] C --> E[使用 -> 或 ->*] ``` 2. **编译检查**:开启编译器警告(如 `-Wall`)可提前捕获此类错误。 3. **语义清晰**:成员指针(`->*`)需配合指向类成员的指针使用,常规开发中较少见。 > 遵循这些规则可避免 95% 以上的相关错误。若需深入理解成员指针机制,建议结合 C++ 的 _Pointer-to-Member_ 特性系统学习[^3][^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值