0722-----C++Primer听课笔记----------虚函数和模板

1.虚函数

  1.1 不含有任何数据成员或者虚函数的class或者struct大小为1含有虚函数的对象在基地址部分有一个vptr,指向虚函数表,因此大小为4个字节。

  1.2 动态绑定的原理:假设派生类和基类存在覆盖的关系(基类中定义了虚函数),那么派生类在虚函数表中,会覆盖掉基类相应的虚函数。当程序执行的时候,根据基类指针找到vptr,根据vptr找到vtable,然后找到相应的版本去执行。所以执行的是覆盖的版本,而具体被哪个版本覆盖是由具体的对象类型所决定的,所以才实现了根据对象的具体类型去调用相应的函数(参考资料:http://blog.csdn.net/haoel/article/details/1948051/)。

  1.3 当类中含有虚函数的时候(不全面),需要把析构函数设为virtual析构函数

    1.3.1 如下例所示,当用基类的指针指向一个派生类的对象时,基类指针会把该内存空间当做是一个基类的对象,对后面派生类的空间不可见,因此,当析构的时候只析构了基类大小的空间,这就造成了资源释放不完全。

#include <iostream>
using namespace std;
/*
 *  基类指针指向子类对象空间
 *  释放基类指针时会释放不完全
 */
class Base{
    public:
        Base(){
            cout << "Base..." << endl;
        }
        ~Base(){
            cout << "~Base..." << endl;
        }
};


class Derived : public Base{
    public:
        Derived(){
            cout << "Derived..." << endl;
        }
        ~Derived(){
            cout << "~Derived..." << endl;
        }
};

int main(int argc, const char *argv[])
{
    Base *bp = new Derived();
    delete bp; //这里只调用了基类的析构函数 派生类的地址空间没有被释放
    return 0;
}

    1.3.2 当把基类的析构函数设为为虚函数时,在回收资源的时候会根据实际的类型动态绑定,将资源全部回收。

  1.4 函数声明为virtual,意味着需要由用户去继承,以重新实现

  1.5 不要试图重定义基类的非virtual函数

  1.6 虚函数尤其是纯虚函数,相当于制定了一种约定、契约,凡是继承并改写(或者实现纯虚函数)的子类,都必须遵守这一约定

#ifndef __THREAD_H__
#define __THREAD_H__
#include <pthread.h>
class Thread{
    public:
        Thread();
        virtual ~Thread(){}
        void start();
        static void* thread_func(void *);
        virtual void run() = 0;
        void join();
    private:
        pthread_t tid;
};


#endif
#include "thread.h"
#include <iostream>

Thread::Thread()
    :tid(-1)
{
}

void Thread::start(){
    pthread_create(&tid, NULL, thread_func, this);
}

void *Thread::thread_func(void *arg){
    Thread *pt = static_cast<Thread *>(arg);
    pt->run(); //动态绑定
}

void Thread::join(){
    pthread_join(tid, NULL);
}
#include "thread.h"
#include <iostream>
#include <unistd.h>

using namespace std;
/*
 * 定义线程类 将执行的函数run定义为纯虚函数
 * 由派生类实现不同的操作
 */

class TestThread : public Thread{
    public:
        void run(){
            while(1){
                sleep(1);
                cout << "hello ..." << endl;
            }
        }
};

int main(int argc, const char *argv[])
{
    TestThread th;
    th.start();
    th.join();
    return 0;
}

 2.模板

  2.1 模板的几个要点:

    a)泛型和模板:泛型就是通用的技术。泛型编程(以独立于任何特定类型的的方式编写代码)不等于模板,模板只是泛型的其中一种手段。

    b)模板就是一种生产函数或者类的模型,以 T add(const T &a, const T &b)为例,当使用 add(1,2)时,编译器会进行模板实参推断产生一个int add(int, int)的模板,当使用 add(string("hello"), string("world"))时,编译器产生string add(const string &, const string&)的版本。

    c)vector不是一个类,而是一个类模板,以它为范本,根据实际调用产生的 vector<int>/vector<string> 才是真正的类。

    d)模板就是一种代码产生器,或者是一个代码输出函数,输入的为类型,输出的是符合该类型运算的类或者函数。

    e)模板的这种代码产生功能,成为一种编译期多态。

    f)模板代码在编译时,只编译那些需要的部分,所以当声明map<Test, int> m;而不做其他使用时,,即使Test不支持 < 操作,仍然没有错误, 如下例。

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

class Test{
    public:
        int a_;
};

int main(int argc, const char *argv[])
{
    map<Test, int> m; //正确 尽管Test不支持< 编译器没有检测

    Test t;
    m[t] = 1; //错误 此时编译器会检测是否支持<

    return 0;
}

  2.2 几个简单的模板

    2.1.1 程序,这里要注意 模板使用template关键字,尖括号内的类型定义可以使用typename,也可以定义为常量,即为非类型模板形参。。

#include <iostream>
#include <string>
#include <vector>
using namespace std;
/*
 * 两个参数的类型可以不同
 */
template <typename T1, typename T2>
T1 add(const T1 &a, const T2 &b){
    return a + b;
}

int main(int argc, const char *argv[])
{
    cout <<  add(2, 2.3) << endl;
    return 0;
}

    2.1.2 程序2。这里T::size_type *p这句话在模板中存在歧义,可以把T::size_type解释成一种类型,所以这里定义了一个指针p,还可以把T::size_type解释成一个变量,所以这样可以看做乘法。解决方案就是在前面加上typename,来说明这是一个定义,而不是乘法。typename T::size_type *p。

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

template <typename T, typename V>
void test(T a, V b){
    //这里存在歧义
    //T::size_type *p;

    typename T::size_type *p;

}
int main(int argc, const char *argv[])
{
    test(string("hello"), 8);
    return 0;
}

  2.3 类模板

    2.3.1 编写类模板的注意事项:

      a)类的声明和实现放到同一个hpp文件中;

      b)所有的函数均为 inline

      c)每个函数在类外实现都要加上模板参数类表;

      d)类名要写完整,例如SmartPtr<T>,不能漏掉尖括号,因为SmartPtr不是完整的类。

    2.3.2 智能指针类模板

#ifndef __SMART_HPP__
#define __SMART_HPP__
#include <stddef.h>

template <typename T>
class Smartptr{
    public:
        Smartptr();
        Smartptr(T *);
        ~Smartptr();
        void resetPtr(T *); //这里T不能为const
        const T *getPtr() const;

        T &operator*();
        const T &operator*()const;

        T *operator->();
        const T *operator->()const;

        operator bool() const;

    private:
        Smartptr(const T&);
        Smartptr operator= (const T&);
        T *ptr_;

};

//智能指针模板类的实现
//每个函数在类外的实现都要加上模板参数列表
template <typename T>
inline Smartptr<T>::Smartptr() //类名包括参数
    :ptr_(NULL)
{
}

template <typename T>
inline Smartptr<T>::Smartptr(T* ptr)
    :ptr_(ptr)
{
}

template <typename T>
inline Smartptr<T>::~Smartptr(){
    delete ptr_;
}

template <typename T>
inline void Smartptr<T>::resetPtr(T *ptr){
    if(ptr_ != ptr){
        delete  ptr_;
        ptr_ = ptr;
    }
}

template <typename T>
inline const T *Smartptr<T>::getPtr()const{
    return ptr_;
}

template <typename T>
inline T &Smartptr<T>::operator*(){
    return *ptr_;
}

template <typename T>
inline const T &Smartptr<T>::operator*()const{
    return *ptr_;
}

template <typename T>
inline T *Smartptr<T>::operator->(){
    return ptr_;
}

template <typename T>
inline const T *Smartptr<T>::operator->() const{
    return ptr_;
}

template <typename T>
inline Smartptr<T>::operator bool() const{
    return ptr_;
}


#endif

#include "smartptr.hpp"
#include <iostream>
#include <assert.h>
using namespace std;

class Animal{
    public:
        Animal(){
            cout << "Animal ..." << endl;
        }
        ~Animal(){
            cout << "~Animal..." << endl;
        }
        void display(){
            cout << "in Animal..." << endl;
        }
};

int main(int argc, const char *argv[])
{
    Smartptr<Animal> pt(new Animal);
    assert(pt); // 这里重载了bool类型

    cout << pt.getPtr() << endl;
    pt.resetPtr(NULL);
    assert(pt == 0);

    cout << "------------" << endl;
    pt.resetPtr(new Animal);
    pt->display();

    return 0;
}

3. 队列类模板

  3.1 编写队列类模板要注意的几点:

    a)typedef 不能写成全局,因为没有使用的时机。以typedef Node<T> *NodePtr 为例,与模板相关的名字都是不完整的,所以直接使用 NodePtr 会找不到符号(编译器不会反向推断 Node<T> 中 T 的类型),使用 Node<T> 这种typedef 是没有意义的。

    b)在Queue的编写中,如果:

template <typename T>
class Node{
    friend class Queue;
    private:
        T data_;
        Node* next_;
};

这表明Node<T>的友元类是Queue,这个Queue是一个普通类,与模板无关。如果:

template <typename T>
class Node{
    friend class Queue<T>;
    private:
        T data_;
        Node* next_;
};

表明此时的Queue不是一个模板类,无法这样使用,这里需要告诉编译器,Queue是一个模板类,因此正确的代码如下:

template <typename T>
class Queue; //前向声明 用于 friend class
template <typename T>
class Node{
    friend class Queue<T>;
    private:
        T data_;
        Node* next_;
};

这两行的作用是告诉编译器,Queue不是一个普通的类,而是一个模板类,所以下面的friend才能使用Queue<T>。   

    c)模板之间互相声明为友元的要点:被声明为 friend 的类,首先应该告知编译器,这是一个模板类,这需要带模板参数类表的前向声明;其次声明 friend 的时候,应该制定具体的模板参数。

  3.2 源程序。

#ifndef __QUEUE_H__
#define __QUEUE_H__

#include <stddef.h>
#include <assert.h>

template <typename T> class Queue; //前向声明 用于 friend class template <typename T> class Node{ friend class Queue<T>; private: T data_; Node* next_; }; template <typename T> class Queue{ public: typedef Node<T> *NodePtr; // Queue(); Queue(const Queue &queue); Queue &operator=(const Queue &queue); ~Queue(); void push(const T &data); void pop(); void clear(); const T &top() const; bool isEmpty()const; size_t getSize()const; private: void copyElements(const Queue &queue); NodePtr head_; NodePtr tail_; size_t size_; }; template <typename T> inline Queue<T>::Queue() :head_(NULL), tail_(NULL), size_(0){ } template <typename T> inline void Queue<T>::copyElements(const Queue &queue){ NodePtr pCur = queue.head_; while(pCur != queue.tail_){ push(pCur->data_); pCur = pCur->next_; } } template <typename T> inline Queue<T> &Queue<T>::operator=(const Queue &queue){ clear(); copyElements(queue); } template <typename T> inline Queue<T>::Queue(const Queue &queue) :head_(NULL), tail_(NULL), size_(0) { copyElements(queue); } template <typename T> inline Queue<T>::~Queue(){ clear(); } template <typename T> inline void Queue<T>::push(const T &data){ NodePtr pt = new Node<T>; pt->data_ = data; pt->next_ = NULL; if(isEmpty()){ head_ = tail_ = pt; } else{ tail_->next_ = pt; tail_ = pt; } size_++; } template <typename T> inline void Queue<T>::pop(){ assert(!isEmpty()); NodePtr pt = head_; head_ = head_->next_; delete pt; } template <typename T> inline void Queue<T>::clear(){ while(!isEmpty()){ pop(); } } template <typename T> inline const T &Queue<T>::top() const{ assert(!isEmpty()); return head_->data_; } template <typename T> inline bool Queue<T>::isEmpty() const{ return head_ == NULL; } template <typename T> inline size_t Queue<T>::getSize()const{ return size_; } #endif #include "queue.hpp" #include <iostream> #include <assert.h> using namespace std; int main(int argc, const char *argv[]) { Queue<int> Q; Q.push(1); Q.push(2); while(!Q.isEmpty()){ cout << Q.top() << endl; Q.pop(); } return 0; }
原文地址:https://www.cnblogs.com/monicalee/p/3861817.html