1. 程式人生 > >類模板以及賦值運算子過載、拷貝建構函式

類模板以及賦值運算子過載、拷貝建構函式

編譯器預設的拷貝建構函式,是發生的淺拷貝,像指標的賦值就會讓指標指向同一個地址空間,析構時就會對同一個地址空間釋放兩次,就會造成程式崩潰.
自定義在模板內的拷貝建構函式:

Queue(const Queue<T> &src)//類物件的拷貝建構函式
    {
        //Queue();
        _pfirst = _prear = NULL;
        QueueItem*p =src._pfirst;
        while (p != NULL)
        {
            addQue(p->_data);
            p =
p->_pnext; } }

上述拷貝建構函式僅僅支援同種型別的物件之間的拷貝構造,對於不同型別的物件之間的拷貝構造就需要建立如下模板(但是必須在定義友元類):

private中需要先定義如下:
template<typename E>
friend class Queue;
template<typename E>
    Queue(const Queue<E>&src)//自定義拷貝建構函式的模板,Queue<E>為新型別,必須先宣告Queue<E>類型別為此型別的友元型別,此模板不能替代上述同類型的拷貝構造
{ cout << "const Queue<E>& 模板" << endl; _pfirst = _prear = NULL; Queue<E>::QueueItem*p = src._pfirst;//必須加Queue<E>這個作用域,否則指標的型別不一樣,會出現錯誤。 while (p != NULL) { addQue(p->_data); p = p->_pnext; } }

此模板在模板外定義:

//不同型別物件的拷貝建構函式的模板外定義
template<typename T>
template<typename E>
Queue<T>::Queue(const Queue<E> &src)
{
    Queue<E>::QueueItem*p = src._pfirst;
    while (p != NULL)
    {
        addQue(p->_data);
        p = p->_pnext;
    }
}

此支援不同型別物件之間的拷貝建構函式不能替代同種型別物件的的拷貝建構函式。
賦值運算子同樣需要自定義,否則就會出現和拷貝構造相同的問題,自定義的相同型別的物件之間的賦值運算子過載函式:

void operator=(const Queue<T> &src)//自定義的賦值運算子的過載函式,避免了編譯器預設的淺拷貝,造成的記憶體洩漏等問題
    {
        cout << "operator Queue<T> &src" << endl;
        if (this == &src)
        {
            return;
        }
        if (_pfirst != NULL)
        {
            while (_pfirst != _prear)
            {
                QueueItem*tmp = _pfirst->_pnext;
                delete _pfirst;
                _pfirst = tmp;
            }
        }
        delete _pfirst;
        _pfirst = _prear = NULL;//必須將頭尾指標指向NULL,否則就會從_pfirst的位置向後新增元素,而_pfirst的位置確是已經釋放的隨機值

        QueueItem*p = src._pfirst;
        while (p != NULL)
        {
            addQue(p->_data);
            p = p->_pnext;
        }
    }

不同型別物件之間的賦值運算子的過載函式模板:

template<typename E>
    void operator=(const Queue<E> &src)//不同型別物件之間的賦值運算
    {
        cout << "operator Queue<T> &src模板" << endl;

        if (_pfirst != NULL)
        {
            while (_pfirst != _prear)
            {
                QueueItem*tmp = _pfirst->_pnext;
                delete _pfirst;
                _pfirst = tmp;
            }
        }
        delete _pfirst;
        _pfirst = _prear = NULL;

        Queue<E>::QueueItem*p = src._pfirst;
        while (p != NULL)
        {
            addQue(p->_data);
            p = p->_pnext;
        }
    }

在沒有給定此模板時,此句int_queue = double_queue會先呼叫不同型別物件直接的拷貝建構函式先生成同種型別得臨時物件,再呼叫同種型別的賦值運算子的過載函式賦值成功,當定義了此 模板就直接例項化此模板一次就完成賦值。

主函式:


int main()
{

    Queue<int> int_queue;
    int_queue.addQue(10);
    int_queue.addQue(20);

    Queue<double> double_queue = Queue<double>();
    double_queue.addQue(30.5);

    Queue<int> int1_queue(int_queue);//相同型別物件之間的拷貝構造,需要自定義拷貝建構函式,否則編譯器預設的造成淺拷貝(指標的賦值)導致出錯
    int1_queue.addQue(40);

    Queue<int> int2_queue(double_queue);//不同型別物件之間的拷貝構造,編譯器不會產生,需要自定義。
    cout <<"int_queue------------------------------------------" << endl;
    int_queue.show();
    cout << "int1_queue----------------------------------------" << endl;
    int1_queue.show();
    cout << "double_queue--------------------------------------" << endl;
    double_queue.show();
    cout << "int2_queue----------------------------------------" << endl;
    int2_queue.show();
    cout << "賦值運算子的過載函式---------------------------------------------" << endl;
    //int_queue = int1_queue;
    //int_queue = Queue<int>(int1_queue);

    //int_queue = double_queue;在沒有給定不同型別的物件之間的賦值運算子的過載函式時,編譯器會先呼叫不同型別物件的拷貝建構函式
    //將double型別的物件構造成Int型別的臨時物件,在呼叫相同型別物件之間的賦值運算子賦值給int_queue
    int_queue = Queue<double>(double_queue);
    int_queue.show();
    return 0;
}

執行結果:
Visual Leak Detector Version 2.4RC2 installed.
const Queue& 模板
int_queue——————————————
10 20
int1_queue—————————————-
10 20 40
double_queue————————————–
30.5
int2_queue—————————————-
30
賦值運算子的過載函式———————————————
operator Queue &src模板
30
No memory leaks detected.
Visual Leak Detector is now exiting.
請按任意鍵繼續…