抽象基类,容器、句柄类与继承--《C++ Primer》笔记

纯虚函数(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<iostream>
#include<set>
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<Item_base> basket;
	std::multiset<Bulk_item> basket2;//存储派生类
	std::multiset<Item_base*> 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);
}

运行insert插入语句有问题,提什么什么乱七八糟instantiated和Rb tree的错误,stl_function.h stl_tree.h stl_multiset.h


15.28

    double sum = ZERO;
    for(std::vector<Item_base>::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<Item_base>::iterator iter = basket.lower_bound(bulk); iter != basket.upper_bound(); iter++){
		std::cout << iter->getIsbn() << " : " << iter->getPrice() << std::endl;
	}*/

15.29 小变动,存指针

std::vector<Item_base*> basket;//变后声明
    basket.push_back(&bulk);//变后插入
sum += (*iter)->getPrice();//变后读取
15.30 解释上两题的差异,看题意吧,一直读不太清楚,题意的求和求得是什么和,如果说 对象总数量那么存指针和存对象肯定有差距。

为了避免重复,都需要判断,一个判断对象的地址是否冲突(怎么判断,预留地址?再建一个容器,遍历容器判断是否有重复?),一个判断对象是否相同(怎么判断,"=="?)


句柄:


//句柄
#include<iostream>
#include<stdexcept>

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<iostream>

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

的net_price() 也不是const,所以没构成虚函数,所以 继承了纯虚函数自己也变成了抽象基类?yes

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:
bool debugState;
};
void Item_base::debugOpen(){ debugState = 1; }void Item_base::debugClose(){ debugState = 0; }
//派生类就不管了,直接继承吧









  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值