抽象基类
在我们平时实现对一个事物的具体的描述,我们可以将它细分为一个又一个的模块,每一个模块有它自己的功能,比如一家店铺,要进行进货,出售,涨价和促销等等不同的功能,但在涨价促销中,相同的模块可能会因为实际情况进而有不同的实现方式,比如对不同的物品总有不同的促销方案,如果有多个模块,只有一个基类将会使该该基类显得臃肿不堪,对我们的开发带来不便,这个时候就需要我们使用抽象基类来解决这个问题。
现在我们有一个书店类我们假设代码如下:
class Quote
{
public:
string book_ISBN;//书的编号
double price;
Quote(const string& name, double p, const string& No) :book_name(name), price(p), book_ISBN(No)
{
}
Quote(const string& No,double p):book_ISBN(No),price(p)
{
}
virtual double net_price(size_t n) const
{
return n * price;
}
virtual string get_ISBN() const
{
return book_ISBN;
}
Quote(double p, string No) :price(p), book_ISBN(No)
{}
virtual ~Quote() = default;
private:
string book_name;
};
现在我们开始设置书店的促销模块,因为促销模块本身没有任何需要实现的具体功能,这个时候我们就能利用一个抽象基类来作为该模块的接口,代码如下:
class CQuote: public Quote
{
public:
CQuote() = default;
CQuote(const string& ISBN,double price,double dis,size_t min):Quote(ISBN,price),discount(dis),min_quanity(min)
{}
double net_price(size_t n) const = 0//纯虚函数
{}
~CQuote()
{}
protected:
size_t min_quanity;
double discount;
};
在此之后我们就可以通过这个抽象基类作为某一个功能的接口,去衍生出实现具体功能的派生类,如下面的Bulk_Quote:
class Bulk_Quote :public CQuote
{
private:
string book_name;
public:
Bulk_Quote(const string& name, string book_ISBN, double price, size_t qry, double dis):
CQuote(book_ISBN,price,dis,qry),book_name(name)
{}
double net_price(size_t n)
{
if (n >= min_quanity)
price = price * discount;
return n * price * discount;
}
~Bulk_Quote()
{}
};
纯虚函数
我们在定义抽象函数的时候,我们不希望创建出来一个具体的类出来,这是我们利用纯虚函数将类转化为抽象基类,抽象基类无法被具例化.换而言之,抽象基类无法new出来。
访问权限
受保护的成员
有时候我们在设计基类的时候,我们可能会遇到一种情况:我们希望一个成员可以被派生类继承,但与此同时我们却又不希望它会被其他类访问,这时候该成员既不能是public,又不能是private,而是一个需要具备这两种类型的部分性质的类型,而这就是受保护类(protected),受保护类有以下性质:
1.和私有成员类似:受保护的成员对于类的成员而言不可访问
2.和公有成员类似,基类的派生类以及类的友元可以访问被保护的成员
3.派生类的成员和友元只能通过只能通过派生类对象来访问基类里面的受保护成员,派生类对一个基类对象中的受保护成员没有任何特权。
对于第三点可能不大好理解,接下来我们通过一段代码来看一下这个问题:
class Quote
{
public:
string book_ISBN;//书的编号
double price;
Quote(const string& name,double p,const string& No):book_name(name),price(p),book_ISBN(No)
{}
Quote(const string& ISBN,double p):book_ISBN(ISBN),price(p)
{}
virtual double net_price(size_t n) const
{
return n*price;
}
virtual string get_ISBN() const
{
return book_ISBN;
}
Quote(double p,string No):price(p),book_ISBN(No)
{}
virtual ~Quote()=default;
private:
string book_name;
protected:
double discount=0,0;
};
class CQuote:public Quote
{
friend void Modify_discont(CQuote& s,double min);
friend void Modify_discont(Quote& s,double min);
public:
CQuote(const string& ISBN,double dis,size_t min,double price):Quote(ISBN,price),discount(dis),min_quanity(min)
{
}
virtual double net_price(size_t n,size_t min,double discount) ;
protected:
size_t min_quanity;
double discount;
};
void Modify_discont(CQuote& s,double min)
{
s.discount=min;
}
void Modify_discont(Quote& s,double min)
{
s.discount=min;
}
当我们将注意点看向这一段代码,基类的受保护对象是discount,随着继承的发生,该对象被派生类继承后,我们在派生类里面定义了两个友元函数,但是传入里面的参数类型不同,一个是派生类的引用,而另一个则是基类的引用,当我们在编译器上看这一段代码的时候,就会发现传入基类引用的函数出现了报错,原因就是我们上面提到的原因。
通俗来说,我们初中历史有一句朗朗上口的话:我附庸的附庸不是我的附庸,而友元也有类似的机制:派生类的友元本身与基类无关,它只能访问派生类里面的成员,而不能对基类里面的成员进行访问。
改变成员的可访问性
有时候我们需要改变派生类继承的某一个成员的访问级别,来达成这一目的,代码实例如下:
class Quote
{
public:
double price;
string book_ISBN;//书的编号
Quote(const string& name,double p,const string& No,double dis):book_name(name),price(p),book_ISBN(No),discount(dis)
{}
Quote(const string& ISBN,double p,double dis):book_ISBN(ISBN),price(p),discount(dis)
{}
virtual string get_ISBN() const
{
return book_ISBN;
}
Quote(double p,string No):price(p),book_ISBN(No)
{}
virtual ~Quote()=default;
private:
string book_name;
protected:
double discount=1.0;
};
class CQuote:public Quote
{
friend void Modify_discont(CQuote& s,double min);
friend void Modify_discont(Quote& s,double min);
public:
using Quote::price;
CQuote(const string& ISBN,double dis,size_t min,double price):Quote(ISBN,price,dis),min_quanity(min)
{
}
protected:
size_t min_quanity;
};
void Modify_discont(CQuote& s,double min)
{
s.discount=min;
}
double net_price(size_t n,CQuote& s)
{
return n*s.price;
}
如上,price因为派生类的继承方式是protected,本来不应该能·被外部函数net_price使用,但是我们利用using使派生类里面的price保持和其声明之前的访问说明符一致。