第36课 经典问题解析三

1. 关于赋值的疑问

(1)疑问

  ①编译器每个类默认重载了赋值操作符

  ②默认的赋值操作符仅完成浅拷贝

  ③当需要进行深拷贝必须重载赋值操作符

  ④赋值操作符拷贝构造函数相同的存在意义

 

【编程实验】默认赋值操作符重载   36-1.cpp

#include <iostream>

using namespace std;

class Test
{

    int* m_pointer;

 public:

     Test(){ m_pointer = NULL; }

     Test(int i){m_pointer = new int(i);}

     Test(const Test& obj){ m_pointer = new int(*obj.m_pointer);}

    
     //赋值4个注意点:

     //1.返回值为引用。2.参数是const引用的对象

     //3.避免自赋值(this!=&obj)。4.返回时*this;
     Test& operator = (const Test& obj)
     {

         if( this != &obj)
         {

             delete m_pointer;

             m_pointer = new int(*obj.m_pointer);

         }  

         return *this;
     }

   
     void print()
     {
         cout << "m_pointer = " << hex << m_pointer << endl;
     }    

     ~Test(){delete m_pointer;}

};

 

int main()
{

    Test t1 = 1;//Test(int i);

    Test t2;    //Test();


    t2 = t1;    //operator=,赋值操作符

    t1.print();  //t1.m_pointer != t2.m_pointer;

    t2.print();  //

    return 0;
}

运行结果:

 

问题分析:

      

 

 

 

一般性原则重载赋值操作符,必然需要实现深拷贝!!!

 

【编程实验】数组类的优化   IntArray

//IntArray.h

#ifndef _INTARRAY_H_
#define _INTARRAY_H_

class IntArray
{
private:
    int m_length;
    int* m_pointer;
    
    IntArray(int len);
    IntArray(const IntArray& obj);
    bool construct();
public:
    static IntArray* NewInstance(int length); 
    int length();
    bool get(int index, int& value);
    bool set(int index ,int value);
    int& operator [] (int index);
    IntArray& operat
or = (const IntArray& obj);
    IntArray& self();
    ~IntArray();
};

#endif

 

//IntArray.cpp

#include "IntArray.h"

IntArray::IntArray(int len)
{
    m_length = len;
}

bool IntArray::construct()
{
    bool ret = true;
    
    m_pointer = new int[m_length];
    
    if( m_pointer )
    {
        for(int i=0; i<m_length; i++)
        {
            m_pointer[i] = 0;
        }
    }
    else
    {
        ret = false;
    }
    
    return ret;
}

IntArray* IntArray::NewInstance(int length) 
{
    IntArray* ret = new IntArray(length);
    
    if( !(ret && ret->construct()) ) 
    {
        delete ret;
        ret = 0;
    }
        
    return ret;
}

int IntArray::length()
{
    return m_length;
}

bool IntArray::get(int index, int& value)
{
    bool ret = (0 <= index) && (index < length());
    
    if( ret )
    {
        value = m_pointer[index];
    }
    
    return ret;
}

bool IntArray::set(int index, int value)
{
    bool ret = (0 <= index) && (index < length());
    
    if( ret )
    {
        m_pointer[index] = value;
    }
    
    return ret;
}

int& IntArray::operator [] (int index)
{
    return m_pointer[index];
}

IntArray& IntArray::operator = (const IntArray& obj)
{
    if( this != &obj )
    {
        int* pointer = new int[obj.m_length];
        
        if( pointer )
        {
            for(int i=0; i<obj.m_length; i++)
            {
                pointer[i] = obj.m_pointer[i];
            }
            
            m_length = obj.m_length;
            delete[] m_pointer;
            m_pointer = pointer;
        }
    }
    
    return *this;
}

IntArray& IntArray::self()
{
    return *this;
}

IntArray::~IntArray()
{
    delete[]m_pointer;
}

 

//main.cpp

#include <iostream>

#include "IntArray.h"

 

using namespace std;

 

int main()

{

    IntArray* a = IntArray::NewInstance(5);

    IntArray* b = IntArray::NewInstance(10);

 

    if(a && b)

    {

        IntArray& array = a->self();

        IntArray& brray = b->self();

       

        cout << "array.length() = " << array.length() << endl; //5      

        cout << "brray.length() = " << brray.length() << endl; //10

 

        array = brray; //赋值

 

        cout << "array.length() = " << array.length() << endl; //10      

        cout << "brray.length() = " << brray.length() << endl; //10       

    } 

   

    delete a;

    delete b;

   

    return 0;

}

运行结果:

 

 

(2)编译器默认提供的函数

  • 不带参的构造函数

  • 拷贝构造函数

  • 默认的赋值操作符

  • 析构函数

    

 

 

(3)一般原则重载赋值操作符,必然需要实现深拷贝!!!

 

2. 关于string的疑问

(1)string类内部维护一个指向数据的char*指针(m_cstr),这里用于存放字符串数据的堆空间地址。因字符串操作(如复制、合并、追加等),

所以这个指针可能在程序运行的过程中发生改变

 

【编程实验】字符串问题1   36-2.cpp

#include <iostream>

 

using namespace std;

 

int main()
{

    string s = "12345";

  
    //程序试图在C的方式访问字符串(不建议这样用!)
    const char* p = s.c_str();//c_str表示C方式的字符串
 
    cout << p << endl; //打印12345

    s.append("abcde"); //p成为野指针,因为追加字符串,可能

                       //导致堆内存的重新分配,从而m_cstr

                       //指的堆内存地址改变了,但p并不知道!

                      
    cout << p << endl;  //仍然指向旧的地址(野指针) 

   

    return 0;

}

运行结果:

 

问题分析:

    

(2)string类内部维护了一个m_length的变量,用于指示字符串中字符的个数。当使用C的方式使用string对象时,这个m_length可能不会自动更新

 

【编程实验】字符串问题2   36-3.cpp

#include <iostream>

 

using namespace std;

 

int main()
{

    const char* p = "12345";

    string s = ""; //s.length==0

    //保留一定量内存以容纳一定数量的字符
    s.reserve(10);//s.length ==0;

    //不要使用C语言的方式操作C++中的字符串
    for(int i=0; i < 5; i++)
    {

        s[i] = p[i];//注意,虽然此时s对象中的字符串内存

                    //确实被赋新的值了。但用这种方式赋值

                    //相等于只是通过指针赋值,s.length不

                    //会自动更新,即仍为0

    }

   

    cout << s.length() << endl; //0

    cout << s.empty() << endl;  //1

    cout << s << endl;  //这里将不会有任何输出,因为s.length=0;

   

    return 0;

}

运行结果:

 

 

问题分析:

      

 

 

3. 小结

(1)在需要进行深拷贝的时候必须重载赋值操作符

(2)赋值操作符拷贝构造函数同等重要的意义

(3)string通过一个数据空间保存字符数据

(4)string通过一个成员变量保存当前字符串的长度

(5)C++开发尽量避开C语言中惯用的编程思想

posted @ 2018-12-23 23:12  梦心之魂  阅读(120)  评论(0编辑  收藏  举报