这里强调是面向对象编程的易错点和常用技巧,严格来说大多数时候使用的封装和继承来完成的对象编程只能算是基于对象的编程,结合对象的继承和多态特性编写的程序才算是面向对象编程。
1.继承的特性
继承涉及到一些复制控制特性,主要如下:
a).不显式调用父类构造函数时,子类构造函数会先隐式调用父类默认构造函数
b).子类析构函数会先隐式调用父类析构函数
c).子类复制构造函数和赋值重载不会隐式调用父类
d).只要子类中存在和父类同样名字的函数,就算子类和父类的函数声明不一样,父类的函数也一定会被覆盖
如下,定义类
class CTest1
{
public:
void Display(int a);
};
class CTest2 : public CTest1
{
public:
void Display(int a, int b);
};
可如下调用
//t21.Display(10); error!
t21.CTest1::Display(10);
t21.Display(10,20);
看以看到,尽管子类没有定义单个参数的重载,仍然不能调用父类Display函数,除非显式调用父类的Display(10)。
2.使用句柄类支持多态
C++有个不好的地方,多态性必须使用指针或引用才能体现出来,即使用指针指向不同的子类对象,这时候虚函数生效会调用不同子类的同一个函数,从而体现多态性,但是这样带来的一个不方便就是对象的生存周期的管理,必须自己管理指针对象。反过来,不使用指针,直接使用对象,可以利用构造和析构函数完成具体的管理,但是这样就丧失了多态性。
为了既可以利用多态,也支持自动管理,我们引入了句柄类。
1.管理基类指针
先看第一种句柄类,这种句柄类主要用来管理基类指针,句柄类类似智能指针,如下:
class CPeopleHandle
{
public:
CPeopleHandle(CPeople& p): m_pPeople(p.Clone()), m_pRefCnt(new int(1))
{
}
virtual ~CPeopleHandle()
{
DeleteData();
}
CPeopleHandle(const CPeopleHandle& h)
{
CopyData(h);
}
CPeopleHandle& operator=(const CPeopleHandle& h)
{
++(*(h.m_pRefCnt));//先增加h的引用计数,以处理自己给自己赋值的情况
DeleteData();
this->m_pPeople = h.m_pPeople;
this->m_pRefCnt = h.m_pRefCnt;
return *this;
}
CPeople* operator->()
{
return m_pPeople;
}
CPeople& operator*()
{
return *m_pPeople;
}
private:
CPeople* m_pPeople;
int* m_pRefCnt;
......
这里我们对基类指针CPeople*做了包装,复制控制、*和->重载都和智能指针类似。不同的在于,定义构造函数如下,
CPeopleHandle(CPeople& p): m_pPeople(p.Clone()), m_pRefCnt(new int(1))
{
}
针对不同的子类,调用他们的Clone虚函数创建对应的对象,例如
class CPeople
{
public:
virtual void Dispaly();
virtual CPeople* Clone();
protected:
int m_age;
};
class CMan : public CPeople
{
public:
virtual void Dispaly();
virtual CMan* Clone();
};
class CWoman : public CPeople
{
public:
virtual void Dispaly();
virtual CWoman* Clone();
};
Clone定义如下
CPeople* CPeople::Clone()
{
return new CPeople(this->m_age);
}
CMan* CMan::Clone()
{
return new CMan(this->m_age);
}
CWoman* CWoman::Clone()
{
return new CWoman(this->m_age);
}
注意,虚函数支持子类返回的类型为子类指针。
这样就使用句柄类管理了不同子类的对象,支持多态且支持引用计数。
可如下使用
vector<CPeopleHandle> vcPeople;
vcPeople.push_back(CPeople(10));//隐式构造CPeopleHandle
vcPeople.push_back(CPeople(20));
vcPeople.push_back(CMan(18));
vcPeople.push_back(CWoman(14));
for (int i=0; i<vcPeople.size(); i++)
{
vcPeople[i]->Dispaly();
}
所有对象统一一种句柄类来包装,是不是很方便?
2.管理和创建基类指针
上面介绍的句柄类主要负责包装基类指针,原始基类对象是自己在类外定义的。那能不能让句柄类做更多的事情?如下,我们只定义类层次结果,然后让句柄类来负责管理基类指针,同时负责创建不同的子类对象。
这里我们假设这样一种需求:
要求判断指定的一段文本中是否存在一个指定的字母,要求支持与或非运算。如对于"abcabdabc,"‘a’ | ('c' & 'd'),返回为真。
1.首先,句柄类封装了基类指针。
2.然后,句柄类还负责创建不同的子类对象。
如下定义继承关系
CQuery为基类,是基本查询类,成员变量m_chQuery为待查询的字符,虚函数IsExist执行查询操作,如下
virtual bool IsExist()
{
return QUERY_STRING.find(m_chQuery)!=string::npos;
}
CNotQuery支持求反运算,包含指向基类的指针,因为句柄类管理基类指针,我们直接换成句柄类,IsExist实现如下
virtual bool IsExist()
{
return !m_query.IsExist();
}
CAndQuery和COrQuery都继承CBinary,支持二元与或操作,包含两个基类指针,即句柄类,IsExist实现分别如下
virtual bool IsExist()
{
return m_query1.IsExist() && m_query2.IsExist();
}
virtual bool IsExist()
{
return m_query1.IsExist() || m_query2.IsExist();
}
这里的IsExist全部定义为private,因为这些类的创建和接口导出都由句柄类管理了,
句柄类定义如下
class CQueryHandle
{
public:
CQueryHandle();
virtual ~CQueryHandle();
CQueryHandle(const CQueryHandle& h);
CQueryHandle& operator=(const CQueryHandle& h);
private:
CQuery *m_pQuery;
int* m_pRefCnt;
void CopyData(const CQueryHandle& h);
void DeleteData();
public:
//重载操作符
friend CQueryHandle operator!(const CQueryHandle& q);
friend CQueryHandle operator&(const CQueryHandle& q1, const CQueryHandle& q2);
friend CQueryHandle operator|(const CQueryHandle& q1, const CQueryHandle& q2);
CQueryHandle(char c);
CQueryHandle(CQuery* p);
bool IsExist();
};
管理基类指针的部分就不说了,和前面的一致,这里注意的是重载操作符和定义两个构造函数以支持类的创建,如下
//统一创建和管理
CQueryHandle::CQueryHandle( char c )
{
m_pQuery = new CQuery(c);
m_pRefCnt = new int(1);
}
CQueryHandle::CQueryHandle( CQuery* p )
{
m_pQuery = p;
m_pRefCnt = new int(1);
}
//操作符重载,隐式构造
CQueryHandle operator!( const CQueryHandle& q )
{
return new CNotQuery(q);
}
CQueryHandle operator&( const CQueryHandle& q1, const CQueryHandle& q2 )
{
return new CAndQuery(q1, q2);
}
CQueryHandle operator|( const CQueryHandle& q1, const CQueryHandle& q2 )
{
return new COrQuery(q1, q2);
}
导出接口IsExist定义如下
bool CQueryHandle::IsExist()
{
return m_pQuery->IsExist();
}
如下使用
CQueryHandle aa= (CQueryHandle('a') | CQueryHandle('x')) & CQueryHandle('0');
cout << aa.IsExist() << endl;
可以看到,这里我们根本没有定义基类和子类对象,基类和子类主要用来定义继承关系,我们直接操作的只有句柄类,基类和子类对于用户来说是私有的。
本文完整演示代码下载链接
原创,转载请注明来自http://blog.csdn.net/wenzhou1219