Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Help on relationship between two classes

29 views
Skip to first unread message

fl

unread,
Oct 6, 2015, 9:33:29 AM10/6/15
to
Hi,

I read a C++ project and cannot see why one class can use another class's
member function. Here is the problem (I don't know why (*next_item) can call
function book()):

void Basket::display(ostream &os) const
{
os << "Basket size: " << items.size() << endl;

// print each distinct isbn in the Basket along with
// count of how many copies are ordered and what their price will be
// upper_bound returns an iterator to the next item in the set
for (const_iter next_item = items.begin();
next_item != items.end();
next_item = items.upper_bound(*next_item))
{
// we know there's at least one element with this key in the Basket
os << (*next_item)->book() << " occurs "
<< items.count(*next_item) << " times"
<< " for a price of "
<< (*next_item)->net_price(items.count(*next_item))
<< endl;
}
}

There are three classes are involved: Basket, Sales_item and Item_base. I
first think Sales_item may access book(), but I don't see the definition.

Here are they:


class Item_base {
friend std::istream& operator>>(std::istream&, Item_base&);
friend std::ostream& operator<<(std::ostream&, const Item_base&);
public:
virtual Item_base* clone() const
{ return new Item_base(*this); }
public:
Item_base(const std::string &book = "",
double sales_price = 0.0):
isbn(book), price(sales_price) { }

std::string book() const { return isbn; }

// returns total sales price for a specified number of items
// derived classes will override and apply different discount algorithms
virtual double net_price(std::size_t n) const
{ return n * price; }

// no work, but virtual destructor needed
// if base pointer that points to a derived object is ever deleted
virtual ~Item_base() { }
private:
std::string isbn; // identifier for the item
protected:
double price; // normal, undiscounted price

};

class Sales_item {
friend class Basket;
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
Sales_item(const Item_base&);

// 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->() const { if (p) return p;
else throw std::logic_error("unbound Sales_item"); }
const Item_base &operator*() const { if (p) return *p;
else throw std::logic_error("unbound Sales_item"); }
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; } }
};


// holds items being purchased
class Basket {
// type of the comparison function used to order the multiset
typedef bool (*Comp)(const Sales_item&, const Sales_item&);
public:
// make it easier to type the type of our set
typedef std::multiset<Sales_item, Comp> set_type;

// typedefs modeled after corresponding container types
typedef set_type::size_type size_type;
typedef set_type::const_iterator const_iter;

void display(std::ostream&) const;

// workaround MS compiler bug: must explicitly pass function address
Basket(): items(&compare) { } // initialze the comparator
void add_item(const Sales_item &item)
{ items.insert(item); }
size_type size(const Sales_item &i) const
{ return items.count(i); }
double total() const; // sum of net prices for all items in the basket
private:
std::multiset<Sales_item, Comp> items;
};

book() is Item_base' member function. Why can (*next_item)->book() be used?

Thanks,

fl

unread,
Oct 6, 2015, 9:40:33 AM10/6/15
to
Sales_item has a pointer to Item_base, but the iterator to std::multiset<Sales_item, Comp>
point to Sales_item, not Sales_item's private member:

Item_base *p; // pointer to shared item

Can you help me?
Thanks,

Öö Tiib

unread,
Oct 6, 2015, 9:49:42 AM10/6/15
to
Because 'Sales_item' has overloaded the member access operator and
there is "drill down behavior" so you don't have to write
'(*next_item)->->book()' or something like that.

Ben Bacarisse

unread,
Oct 6, 2015, 10:23:45 AM10/6/15
to
fl <rxj...@gmail.com> writes:

Replying to your own followup so everything is double quoted.

> On Tuesday, October 6, 2015 at 9:33:29 AM UTC-4, fl wrote:
<snip>
>> void Basket::display(ostream &os) const
>> {
<snip>
>> for (const_iter next_item = items.begin();
>> next_item != items.end();
>> next_item = items.upper_bound(*next_item))
>> {
>> // we know there's at least one element with this key in the Basket
>> os << (*next_item)->book() << " occurs "

items is a multiset of Sales_item, so *next_item is if type const Sales_item.

<snip>
>> << endl;
>> }
>> }
<snip>
>> class Item_base {
<snip>
>> std::string book() const { return isbn; }
<snip>
>> };
>>
>> class Sales_item {
>> friend class Basket;
>> public:
<snip>
>> const Item_base *operator->() const { if (p) return p;
>> else throw std::logic_error("unbound Sales_item"); }

This operator is the key.

<snip>
>> };

<snip>
>> // holds items being purchased
>> class Basket {
<snip>
>> private:
>> std::multiset<Sales_item, Comp> items;
>> };
>>
>> book() is Item_base' member function. Why can (*next_item)->book() be
>> used?

So, *next_item is if type const Sales_item but Sales_item defines
operator-> so an expression of the form x->m is interpreted as
(x.operator->())->m for a class object x. I.e. (*next_item)->book()
means

((*next_item).operator->())->book();

Do you see it now?

<snip>
--
Ben.
0 new messages