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; }

浙公网安备 33010602011771号