纯虚函数(pure virtual function),包含纯虚函数的类是抽象基类(abstract base class)。不能创建抽象类型的对象(object)。
方法,在函数形参表后面写上 = 0,如果写1,error: invalid pure specifier (only ‘= 0’ is allowed) before ‘;’ token
使用目的,防止用户建立Disc_item对象,不让用户在Disc_item上做任何操作。
套用原例有问题:
class Disc_item : public Item_base {
public:
double net_price(std::size_t) const = 0;
};
原例似乎忽略了最基本的问题,virtual关键字,但是在一个
前提下是对的,就是其
基类Item_base先声明过,现在Disc_item
默认是virtual。。。
class Item_base{
public:
virtual double net_price(std::size_t) const;
};
Disc_item discounted; //error:can't define a Disc_item object
Bulk_item bulk; //ok:Disc_item subobject within Bulk_item
15.27,编译器给的结果
error: cannot declare variable ‘discounted’ to be of abstract type ‘Disc_item’
note: because the following virtual functions are pure within ‘Disc_item’:
note: virtual double Disc_item::net_price(size_t) const
容器与继承
据说派生类被sliced down成基类存进基类容器,不过都没试成。
还可以将基类对象显式强制转换为派生类对象,解决方法是存基类指针(还要注意容器存在,对象就必须存在)。
/*容器与继承containerDerived.cpp*/
#include
#include
class Item_base{
public:
Item_base(const std::string &book = "", double sales_price = 0.0) :
isbn(book), price(sales_price) {}
private:
std::string isbn;
double price;
};
class Bulk_item : public Item_base {
public:
Bulk_item(const std::string& book = "", double sales_price = 0.0) :
Item_base(book, sales_price) {}
};
int main(){
std::multiset basket;
std::multiset basket2;//存储派生类
std::multiset basket3;
Item_base base;
Bulk_item bulk;
basket.insert(base); //ok: add copy of base to basket
//basket.insert(bulk); //ok: but bulk sliced down to its base part
basket2.insert(base);
basket2.insert(bulk);
basket3.insert(base);
basket3.insert(bulk);
}
15.28
double sum = ZERO;
for(std::vector::iterator iter = basket.begin(); iter != basket.end(); iter++){
std::cout << iter->getIsbn() << " : " << iter->getPrice() << std::endl;
sum += iter->getPrice();
}
std::cout << " sum : " << sum << std::endl;
/*
//不同isbn的分类怎么办,有一个功能,就是同一健下连续的,一块取出来
//不过还是不能根据ISBN分类,相同key也是以bulk对象为基础的,还区分不了ISBN,而且,这是vector,vector不能用这个方法要multiset和multimap才行
for(std::vector::iterator iter = basket.lower_bound(bulk); iter != basket.upper_bound(); iter++){
std::cout << iter->getIsbn() << " : " << iter->getPrice() << std::endl;
}*/
std::vector basket;//变后声明
basket.push_back(&bulk);//变后插入
sum += (*iter)->getPrice();//变后读取
15.30 解释上两题的差异,看题意吧,一直读不太清楚,题意的求和求得是什么和,如果说
对象总数量,
那么存指针和存对象肯定有差距。
为了避免重复,都需要判断,一个判断对象的地址是否冲突(怎么判断,预留地址?再建一个容器,遍历容器判断是否有重复?),一个判断对象是否相同(怎么判断,"=="?)
句柄:
//句柄
#include
#include
class Item_base{
public:
Item_base(const std::string &book = "", double sales_price = 0.0) :
isbn(book), price(sales_price) {}
std::string getIsbn() {return isbn;}
double getPrice() {return price;}
virtual double net_price(std::size_t n = 0) const { return n * price; }
virtual Item_base* clone() const { return new Item_base(*this); }//复制未知类型
private:
std::string isbn;
double price;
};
class Bulk_item : public Item_base {
public:
Bulk_item(const std::string& book = "", double sales_price = 0.0, std::size_t qty = 0.0, double disc_rate = 0.0) :
Item_base(book, sales_price), quantity(qty), discount(disc_rate) {}
Bulk_item* clone() const { return new Bulk_item(*this); }//虚函数例外:如果基类中返回基类指针,返回派生类指针
public:
double net_price(std::size_t n = 0) const {
if (n > quantity)
return Item_base::net_price() * discount;//直接调用基类的现成功能
return Item_base::net_price();
}
private:
std::size_t quantity;
double discount;
};
//use counted handle class for the Item_base hierarchy
class Sales_item{
public:
//default constructor:unbound handle
Sales_item() : p(0), use(new std::size_t(1)) { }
//attaches a handle to a copy of the Item_base object
//有了clone()后,编写的构造函数
Sales_item(const Item_base& base) : p(base.clone()), use(new std::size_t(1))
{ /*p = new Item_base(base); use = new std::size_t(1);*/ }
//copy control members to manage the use count and pointers
Sales_item(const Sales_item &i) :
p(i.p), use(i.use) { ++*use; }
~Sales_item() { decr_use(); }
Sales_item& operator=(const Sales_item&);
//member access operators 获取成员的操作符
const Item_base *operator->() { if(p) return p; else throw std::logic_error("unbound Sales_item");}
const Item_base &operator*() {if(p) return *p; else throw std::logic_error("unbound Sales_item");}
public://自己定义的测试用小接口
Item_base* getItem() { return p; }
std::size_t getUseCount() { return *use; }//返回的是副本
std::size_t* getUseCountAddr() { return use; }//返回的是原地址
private:
Item_base *p; //pointer to shared item
std::size_t *use; //pointer to shared use count
//called by both destructor and assignment operator to free pointers
void decr_use()
{ if (--*use == 0) { delete p; delete use; } }
};
//use-counted assignment operator; use is a pointer to a shared use count
Sales_item& Sales_item::operator=(const Sales_item &rhs){//赋值过程,计数器加,原计数器减,赋值
++*rhs.use;
decr_use();
p = rhs.p;
use = rhs.use;
return *this;
}
int main() {
//bind a handle to a Bulk_item object
Sales_item item(Bulk_item("0-201-82470-1", 35, 33, .20));
std::cout << item->net_price() << std::endl; //virtual call to net_price function
Item_base base;
Sales_item s(base);
Sales_item s2(s);
std::cout << "base addr: " << &base << std::endl;
std::cout << "s addr: " << &s << std::endl;
std::cout << "s2 addr: " << &s2 << std::endl;
std::cout << "===========================================" << std::endl;
std::cout << "TEST: s.p and s2.p are same~!" << std::endl;
std::cout << "s.getItem(): " << s.getItem() << std::endl;
std::cout << "s2.getItem(): " << s2.getItem() << std::endl;
std::cout << "s和s2的计数器地址一样,计数值一样" << std::endl;
std::cout << "s.getUseCountAddr():\t" << s.getUseCountAddr() << "\tvalue: " << s.getUseCount() << std::endl;
std::cout << "s2.getUseCountAddr():\t" << s2.getUseCountAddr() << "\tvalue: " << s2.getUseCount() << std::endl;
}
一般虚函数要求返回类型一致,但有例外:基类返回指针,派生类就能返回派生类指针。
15.32 一种调试策略,debug虚函数,显示各个类的数据成员。。。原来这他妈的是惊喜啊~!
还有打印方法,虚函数嵌套(用了多次,不知道派生类的虚函数强行调用基类版本理论上好不好),也是个策略
忘了,debug也该价格debug
//实现debug虚函数,显示各个类的数据成员。还有个注意点,派生类的数据成员会多,所以要有策略,为了方便嵌套,返回ostream吧,还有endl的使用。
//派生类不能访问基类部分的成员,改成protected吧?有别的方法吗
//为了统一,我不搞endl了,用户自己隔离吧
class Item_base{
public:
//虚函数,显示各数据成员,我还是把ostrem也参数吧
virtual std::ostream& debug(std::ostream& os) {
os << isbn << "\t" << price << "\t";//为了统一,我不搞endl了,用户自己隔离吧
return os;
}
};
class Bulk_item : public Item_base{
public:
std::ostream& debug(std::ostream& os) {
return Item_base::debug(os) << min_qty << "\t" << limit << "\t" << discount << "\t";
}
};
/*15_33.cpp 加入Disc_item"抽象基类"的版本,Disc_item是否应该实现clone()?我觉得不应该吧,因为他不能有对象*/
#include
class Disc_item : public Item_base {
public:
Disc_item(const std::string &book = "", double price = 10.0):
Item_base(book, price),min_qty(10), limit(20), discount(0.90) {}
Disc_item* clone() const { return new Disc_item(*this); }//用复制构造函数new一个对象。。。
public:
double net_price(std::size_t n) = 0{
if(n < min_qty)
return Item_base::net_price(n);//静态调用基类虚函数
else if(n > limit)
return Item_base::net_price(n - limit) + price * limit * discount;
return Item_base::net_price(n) * discount;//参数不能是(n * discount),因为size_t是整型,discount被取整了
}
public:
std::ostream& debug(std::ostream& os) const = 0{
return Item_base::debug(os) << min_qty << "\t" << limit << "\t" << discount << "\t";//为了统一,我不搞endl了,用户自己隔离吧
}
private:
std::size_t min_qty;//达到折扣需要的最少采购量
std::size_t limit;//超过限度就不能打折。
double discount;
static const double ORIGIN = 1.0;//ORIGIN代表不打折,为什么都要求static
};
这个DIsc_item怎么弄成抽象基类????写俩 = 0 ?pe15_33.cpp:35:34: error: pure-specifier on function-definition
顺便,这个不错
http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fcplr142.htm
根据这上边说明,想标出 = 0 的纯虚函数,不能有函数定义 {}
Confused:关于虚函数
class Item_base{
virtual double net_price(std::size_t n) const {return n * price;}
};
class Disc_item : public Item_base{
double net_price(std::size_t n) = 0;//error
double net_price(std::size_t n) const = 0;//error too
};
pe15_33.cpp: In member function ‘virtual Bulk_item* Bulk_item::clone() const’:
pe15_33.cpp:53:55: error: cannot allocate an object of abstract type ‘Bulk_item’
pe15_33.cpp:49:35: note: because the following virtual functions are pure within ‘Bulk_item’:
pe15_33.cpp:36:9: note: virtual double Disc_item::net_price(size_t) const
I got it ~!
每个const都是重点,因为后续
class Bulk_item : public Disc_item
const不匹配和参数不匹配都导致不能动态绑定
===================================================================================================================================
分界线,我要换风格了,必须把类成员的定义和声明分开了,要不然看起来有点乱,copy代码也不方便,最好带着作用域Base::一起copy过来。
第一种方法:第二个传入bool值,根据1和0决定是否打印
std::ostream& Item_base::debug(std::ostream& os, bool open) const {//virtual只能在class内,也就是声明,定义就不行了
if(open)
os << isbn << "\t" << price << "\t";
return os;
}
std::ostream& Bulk_item::debug(std::ostream& os, bool open) const {
if(open)
return Item_base::debug(os, open) << min_qty << "\t" << limit << "\t" << discount << "\t";//为了统一,我不搞endl了,用户自己隔离吧
else
return os;
}
第二种方法:加入一个状态,通过debugOpen()和debugClose()或者debug(1) debug(0)之类的方法转换状态
class Item_base{
public:
Item_base::Item_base(const std::string &book = "", double sales_price = 0.0):
isbn(book), price(sales_price), debugState(0){}//构造函数初始化,debug状态默认0
protected: