zoukankan      html  css  js  c++  java
  • 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; }
  • 相关阅读:
    (转)乐观的并发策略——基于CAS的自旋
    mysql避免插入重复数据
    Java_Web使用简单的批处理操作
    Java中jar命令详解
    使用文档注释(javadoc)
    APP和WEB元素定位方法
    RF(二)RF常用库介绍
    RF(一)RF的安装步骤
    javascript匿名函数及闭包深入理解及应用
    javascript简介
  • 原文地址:https://www.cnblogs.com/monicalee/p/3861817.html
Copyright © 2011-2022 走看看