码迷,mamicode.com
首页 > 编程语言 > 详细

【C/C++】操作符重载

时间:2016-08-11 00:48:32      阅读:255      评论:0      收藏:0      [点我收藏+]

标签:

常见问题

Q1. 下列运算符,在C++语言中不能重载的是( )。

  A. *  B. ?:  C. ::  D. delete

Q2. 编写类String的构造函数、析构函数和赋值函数。

Q3. 复制构造函数与赋值运算符的区别是什么?

Q4. 下述代码的输出结果是什么?

 1 #include <iostream>
 2 using namespace std; 
 3 class X
 4 {
 5 public:
 6     X() { cout << "constructor" << endl; }
 7     static void * operator new (size_t size)
 8     {
 9         cout << "new" << endl; 
10         return ::operator new(size); 
11     }
12     static void operator delete (void * pointee)
13     {
14         cout << "delete" << endl; 
15         ::operator delete(pointee); 
16     }
17     ~X() { cout << "destructor" << endl; }
18 }; 
19 int main()
20 {
21     X * px = new X(); 
22     delete px; 
23     return 0; 
24 }

Q5. 如何限制栈对象的生成?如何限制堆对象的生成?

操作符重载

  操作符重载函数的名字为operator后跟着所定义的操作符的符号。像任何其他函数一样,操作符重载函数有一个返回值和一个形参表。形参表必须具有与该操作符数目相同的形参(如果操作符是一个类成员,则包括隐式this形参)。比如赋值是二元运算,所以该操作符函数有两个形参:第一个形参对应着左操作数,第二个形参对应右操作数。

  大多数操作符可以定义为成员函数或非成员函数。当操作符为成员函数时,它的第一个操作数隐式绑定到this指针。有些操作符(包括赋值操作符)必须是类的成员函数。比如赋值就必须是类的成员,所以this绑定到指向左操作数的指针。因此,赋值操作符接受单个形参,且该形参是同一类类型的对象。右操作数一般作为const引用传递。

1. 可重载的操作符

  大部分操作符是可以重载的,但不是所有的操作符都是可重载的。不能重载的操作符有以下四个:

    ::  .*  .  ?:

  简单的总结:带"点"的都不能重载

2. 赋值操作符重载

【示例】已知String类的原型如下,编写类String的构造函数、析构函数和赋值函数。

 1 class String 
 2 {
 3 public:
 4     // 普通构造函数 
 5     String(const char *str = NULL); 
 6     // 拷贝构造函数 
 7     String(const String &other); 
 8     // 析构函数 
 9     ~String(); 
10     // 赋值函数 
11     String & operator = (const String &other); 
12 private:
13     // 用于保存字符串 
14     char *m_data; 
15 }; 

(1) String 的析构函数

  为防止内存泄漏,我们需要定义一个析构函数。当一个String对象超出它的作用域时,这个析构函数将会释放它所占用的内存。代码如下:

1 String::~String()
2 {
3     delete [] m_data; 
4     // 由于m_data是内部数据类型,也可以写成delete m_data 
5 }

(2) String的构造函数

  这个构造函数可以帮助我们根据一个字符串常量创建一个String对象。这个构造函数首先分配了足量的内存,然后把这个字符串常量复制到这块内存,代码如下:

 1 String::String(const char *str)
 2 {
 3     if (str == NULL) // 若能加上NULL判断则更好 
 4     {
 5         m_data = new char [1]; 
 6     }
 7     else 
 8     {
 9         int length = strlen(str); 
10         m_data = new char [length + 1]; // 思考为什么要加1 
11         strcpy(m_data, str); 
12     }
13 }

  strlen函数返回这个字符串常量的实际字符数(不包括NULL终止符),然后把这个字符串常量的所有字符赋值到我们在String对象创建过程中为m_data数据成员新分配的内存中。有了这个构造函数后,我们可以像下面这样根据一个字符串常量创建一个新的String对象:

  String str("hello");

(3) String的拷贝构造函数

  所有需要分配系统资源的用户定义的类型都需要一个拷贝构造函数。拷贝构造函数还可以帮助我们在函数调用中以传值的方式传递一个String参数,并且当一个函数以值的形式返回String对象时实现"返回时复制"。

 1 String::String(const String &other)
 2 {
 3     if (other.m_data == NULL)
 4     {
 5         m_data = new char [1]; 
 6     }
 7     else 
 8     {
 9         int length = strlen(other.m_data); 
10         m_data = new char [length + 1]; 
11         strcpy(m_data, str); 
12     }
13 } 

(4) String的赋值函数

  赋值函数代码如下所示:

String & String::operator = (const String & other)
{
    if (this == &other) // 检查自赋值
        return *this; 
    delete [] m_data; // 释放原有的内存资源
    int length = strlen(other.m_data); 
    m_data = new char [length + 1]; // 分配新的内存资源
    strcpy(m_data, other.m_data); 
    return *this;  
}

  赋值操作符重载需要注意:

  A. 是否把返回值的类型声明为该类型的引用,并在函数结束前返回实例自身的引用。只有这样,才可以允许连续赋值。否则如果函数的返回值是void,假设有三个String的对象,str1、str2和str3,在程序中语句str1 = str2 = str3将不能通过编译。

  B. 是否把传入的参数的类型声明为常量引用。如果传入的参数不是引用而是实例,那么从形参到实参会调用一次拷贝构造函数。把参数声明为引用可以避免这样的无谓消耗,能提高代码的效率。同时,我们在赋值运算符函数内是不会改变传入的实例的状态,因此应该为传入的引用参数加上const关键字。

  C. 是否记得释放实例自身有的内存。如果忘了在分配新的内存之前释放自身已有的空间,将出现内存泄露。

  D. 是否判断传入的参数和当前的实例(*this)是不是同一个实例。如果是同一个,则不进行赋值操作,直接返回。如果事先不判断,就进行赋值,那么在释放实例自身的内存时就会导致严重的问题:当*this和传入的参数是同一个实例,那么一旦释放了自身的内存,传入的参数的内存也同时被释放了,因此再也找不到需要赋值的内容了。

3. 输出操作符<<的重载

 

4. operator new 与 operator delete 的重载

【C/C++】操作符重载

标签:

原文地址:http://www.cnblogs.com/xiaoxxmu/p/5759110.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!