为了账号安全,请及时绑定邮箱和手机立即绑定

面向对象编程和泛型编程

标签:
面向对象编程
  • 继承,动态绑定数据对象一起成为了面向对象编程的基础

1、用类进行数据抽象——抽象和封装
2、用类派生从一个类继承另一个类:派生类继承基类的成员——代码复用
3、关键思想:多态,动态绑定能够在运行时决定调用的是基类定义的函数还是子类定义的函数——接口复用

  • c++中通过基类的引用和指针调用虚函数时发生动态绑定
  • 出来构造函数,任何非static函数都可以为虚函数,关键字virtual只在类内部成员函数声明的时候出现不能用在外部的类成员函数定义中
  • 派生类的虚函数声明必须和基类保持一致,例外,基类的虚函数返回的对基类的引用的情况下,这时候派生虚函数可以返回对派生类的引用
  • 覆盖虚函数机制,即强制调用基类的虚函数p->base::foo();
  • 友元关系不能继承
  • static成员,整个继承层次中只有一个这样的成员
  • 派生类的构造函数

1、合成的派生类默认的构造函数,会先调用基类的构造函数初始化基类部分,后初始派生类的数据成员,最后执行派生类的构造函数内容
2、自定义的默认构造函数和之前一样,但是会隐式的调用基类的构造函数,也可以显示的调用基类的构造函数

  • 如果自定义的派生类复制构造函数(赋值操作符也一样,不过一般是在函数一开始先调用基类定义的赋值操作符)一般应该显式的调用基类的构造函数初始化对象的基类部分,如果没有显示调用复制构造函数编译器会调用默认的构造函数初始基类部分,导致怪异的局面
    drived(const drived &d):base(d){}
  • 派生类的 析构函数会自动调用其基类的析构函数,调用顺序与构造函数相反,即先析构派生类部分,再调用基类的析构函数
  • 构造函数和赋值操作符不应该是虚函数
  • 构造函数和析构函数中调用的虚函数,调用的是自身版本的虚函数,仔细想想这是很科学的
  • 容器与继承引出了一个问题

1、即定义一个基类的容器,如果在里面放置子类,会导致子类被slice,只有其基类部分被保存了
2、如果定义的是子类的容器,可以将基类进行适当转换放进去,但在使用的时候会出问题,因为此时转换后的子类其子类部分是未初始化的。
3、还有就是可以采用保存基类的指针类型的容器,但此时需要对其指向的对象进行额外管理

  • 合理的解决方案——句柄类,类似智能指针,但提供了对其包装类的接口
  • 在定义句柄类时候,需要进行适当区分,其复制构造函数实际是指进行一份资源的操作,而赋值构造函数实际可能在操作两份资源(当然徐泰特别考虑自我赋值的情况)
  • 其次要定义解引用和->操作符

const myClass * operator->() const;
const myClass & operator*() const;

  • 为了使类支持句柄类操作,必须为该类提供一个clone的虚函数,该函数返回类自身的一个副本,这样在句柄类的够着函数,就不需要知道传入的参数是基类还是子类了,只需要调用这个clone()函数即可
  • 这里给一个multiset的用法,提供比较函数
bool compare(const sale_item &lhs,const sale_item &rhs)
{return lhs->book()<rhs->book()}
typedef bool (*Comp)(const sale_item &lhs,const sale)item &rhs)
std::multiset<sale_item,Comp> items(compare)
模版与泛型编程
  • 泛型编程,即独立于任何特定类型的方式编写代码,其基础是模版,其实现是依赖于某种形式的多态性,面向对象的编程依赖的多态称为运行时多态,而泛型编程依赖的是编译时多态。
  • 模版的定义和模版的使用要放在同一个文件
  • 调用函数模版不必显示指定参数类型,但是调用类模版时需显示的指定实参,如vector<int>
  • 类除了定义数据、函数成员还可以定义类型成员,在函数模版内使用了这些类型必须显示指定其为一个类型
template <class T>
void Foo()
{typename T::size_type *p ;}
  • 类模版成员函数

template<class T> ret_type myClass<T>::memberfcn()

  • 类模版的实例化,对应的在定义对象的时候会实例化类的构造函数,但其他成员函数要是还没用到,不会进行相应的实例化,用到才实例化。
  • handle类行为类似指针,复制handle对象不会复制其基础对象,复制之后,两个handle指向同一个基础对象。创建一个handle对象,用户需要传递属于有handle管理类型的动态分配对象的地址,从此刻起,handle将拥有这个对象,并且一旦不在有handle对象与该对象关联,handle类负责删除该对象。
#ifndef MYQUEUE
#define MYQUEUE
#include <iostream>
using namespace std;
//声明模板类
template<class T> class Queue;
template<class T> class Queue_item;
//声明模板成员
//template<class T> 
//ostream& operator<<(ostream &out,const Queue<T> &q);
//定义Queue_item类,该类成员都是private,故需要提供友元对其进行访问
template<class T>
class Queue_item{
  T item;
  Queue_item *next;
  Queue_item(const T &qi):item(qi),next(0){}
  friend class Queue<T>;    //queue需要访问其item和next
  //friend ostream& operator<< <T> (ostream &out,const Queue<T> &q);//进行输出操作
  //template<class Type> friend ostream& operator<<(ostream& output,const Queue<Type> &q);
  //friend ostream& operator<<<int>(ostream& output,const Queue<int> &q);
};
//定义Queue类
template<class T>
class Queue
{
  friend std::ostream& operator<<(std::ostream & out,const Queue<T> &q);
public:
  //默认构造,空队列
  Queue():head(0),tail(0){}    
  //复制构造函数
  Queue(const Queue &q):head(0),tail(0){copy_items(q);}
  //初始化构造函数
  template<class it>
  Queue(it beg,it ed):head(0),tail(0){copy_items(beg,ed);}
  //赋值操作符
  Queue& operator=(const Queue &q);
  //析构函数
  ~Queue(){destroy();}
  //功能函数
  template<class it>void assign(it beg,it ed);
  T& front(){if(head != NULL) return head->item;throw std::runtime_error("empty queue");}
  const T& front()const {if(head != NULL) return head->item;throw std::runtime_error("empty queue");}
  void push(const T &qi);
  void pop();
  bool empty(){return head == 0;}
private:
  Queue_item<T> *head;
  Queue_item<T> *tail;
  void copy_items(const Queue &q);
  template<class it> void copy_items(it,it);
  void destroy();
};
//实现push操作
template<class T> void Queue<T>::push(const T &qi)
{
  Queue_item<T> *p = new Queue_item<T>(qi);
  if (empty())    
  {
      head = tail = p;
  }
  else
  {
      tail->next = p;
      tail = p;
  }
}
//实现pop操作
template<class T> void Queue<T>::pop()
{
  if (empty()) return;
  Queue_item<T> *p = head;
  head = head->next;
  delete p;
}
//这个是用于复制构造函数,故刚刚开始队列为空,不需要考虑自身的情况
template<class T> void Queue<T>::copy_items(const Queue &q)
{
  Queue_item<T> *p = q.head;
  while(p!=NULL)
  {
      push(p->item);
      p = p->next;
  }
}
//实现清除队列元素
template<class T> void Queue<T>::destroy()
{
  while(!empty())
      pop();        
}
template<class T> template<class it> void Queue<T>::copy_items(it beg,it ed)
{
  while(beg!=ed)
      push(*beg++);
}
template<class T> template<class it> void Queue<T>::assign(it beg,it ed)
{
  destroy();
  copy_items(beg,it);
}
template<class T> Queue<T> & Queue<T>::operator=(const Queue<T> &q)
{
  if (q.head != head && q.tail!=tail)
  {
      destroy();
      copy_items(q);
  }
  return *this;
}
//针对Queue重载<<操作符,实现输出
//template<class T>
//ostream& operator<< (ostream &out,const Queue<T> &q)
//{
//    out<<"< ";
//    Queue_item<T> *it = q->head;
//    while(it!=NULL)
//    {
//        out<<it->item<<" ";
//        it = it->next;
//    }
//    out<<">"<<std::endl;
//    return out;    
//}
#endif

怎么样,面向对象编程和泛型编程掌握了吗?
图片描述

点击查看更多内容
8人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
7
获赞与收藏
466

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消