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; }
  • 相关阅读:
    【Java EE 学习 36】【struts2】【struts2系统验证】【struts2 ognl值栈】【struts2 ongl标签】【struts2 UI标签】【struts2模型驱动和令牌机制】
    【Java EE 学习 35 下】【struts2】【struts2文件上传】【struts2自定义拦截器】【struts2手动验证】
    【Java EE 学习 35 上】【strus2】【类型转换器】【struts2和Servlet API解耦】【国际化问题】【资源文件乱码问题已经解决】
    【Java EE 学习 34】【struts2学习第一天】
    【JavaScript中的正则表达式】
    【Java EE 学习 33 下】【validate表单验证插件】
    【Java EE 学习 33 上】【JQuery样式操作】【JQuery中的Ajax操作】【JQuery中的XML操作】
    【Java EE 学习 32 下】【JQuery】【JQuey中的DOM操作】
    【Java EE 学习 32 上】【JQuery】【选择器】
    【Java EE 学习 31】【JavaScript基础增强】【Ajax基础】【Json基础】
  • 原文地址:https://www.cnblogs.com/monicalee/p/3861817.html
Copyright © 2011-2022 走看看