Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Why the destructor executes after its friend operator (in which thereis a copy constructor) runs

Reply
Thread Tools

Why the destructor executes after its friend operator (in which thereis a copy constructor) runs

 
 
fl
Guest
Posts: n/a
 
      01-05-2013
Hi,

I am learning C++ with "C++ Primer" 4th edition. There is a class derivation example in Chapter 15. When I step through the code, I find that the base class destructor
,,,,,,,
~Query() { decr_use(); }
,,,,,,,
executes after the derived class AndQuery() (which is derived from BinaryQuery, which is then derived from Query_base) constructor runs:
,,,,,,,,,,,,,,,,,,
AndQuery(Query left, Query right): BinaryQuery(left, right, "&") { }
,,,,,,,,,,,,,,,,,,

The above problem appears when I run

Query orq = Query(sought1) & Query(sought2);

It triggers the "and" operator. I see that it copies "left" to "lhs" etc.:

BinaryQuery(Query left, Query right, std::string op): lhs(left), rhs right, oper(op) { }

The next call
~Query() { decr_use(); }
puzzles me a lot.

Could you explain it to me?

Thanks in advance
................
// private, abstract class acts as a base class for concrete query types
class Query_base {
friend class Query;
protected:
typedef TextQuery::line_no line_no;
virtual ~Query_base() { }
private:
// eval returns the |set| of lines that this Query matches
virtual std::set<line_no>
eval(const TextQuery&) const = 0;
// display prints the query
virtual std:stream&
display(std:stream& = std::cout) const = 0;
};


// handle class to manage the Query_base inheritance hierarchy
class Query {
// these operators need access to the Query_base* constructor
friend Query operator~(const Query &);
friend Query operator|(const Query&, const Query&);
friend Query operator&(const Query&, const Query&);
public:
Query(const std::string&); // builds a new WordQuery

// copy control to manage pointers and use counting
Query(const Query &c): q(c.q), use(c.use) { ++*use; }
~Query() { decr_use(); }
Query& operator=(const Query&);

// interface functions: will call corresponding Query_base operations
std::set<TextQuery::line_no>
eval(const TextQuery &t) const { return q->eval(t); }
std:stream &display(std:stream &os) const
{ return q->display(os); }
private:
Query(Query_base *query): q(query),
use(new std::size_t(1)) { }
Query_base *q;
std::size_t *use;
void decr_use()
{ if (--*use == 0) { delete q; delete use; } }
};

inline Query& Query:perator=(const Query &rhs)
{
++*rhs.use;
decr_use();
q = rhs.q;
use = rhs.use;
return *this;
}

inline std:stream&
operator<<(std:stream &os, const Query &q)
{
return q.display(os);
}

class WordQuery: public Query_base {
friend class Query; // Query uses the WordQuery constructor
WordQuery(const std::string &s): query_word(s) { }

// concrete class: WordQuery defines all inherited pure virtual functions
std::set<line_no> eval(const TextQuery &t) const
{ return t.run_query(query_word); }
std:stream& display (std:stream &os) const
{ return os << query_word; }
std::string query_word; // word for which to search
};

inline
Query::Query(const std::string &s): q(new WordQuery(s)),
use(new std::size_t(1)) { }

class NotQuery: public Query_base {
friend Query operator~(const Query &);
NotQuery(Query q): query(q) { }

// concrete class: NotQuery defines all inherited pure virtual functions
std::set<line_no> eval(const TextQuery&) const;
std:stream& display(std:stream &os) const
{ return os << "~(" << query << ")"; }
const Query query;
};

class BinaryQuery: public Query_base {
protected:
BinaryQuery(Query left, Query right, std::string op):
lhs(left),
rhs(right),
oper(op)
{ }

// abstract class: BinaryQuery doesn't define eval
std:stream& display(std:stream &os) const
{ return os << "(" << lhs << " " << oper << " "
<< rhs << ")"; }

const Query lhs, rhs; // right- and left-hand operands
const std::string oper; // name of the operator
};

class AndQuery: public BinaryQuery {
friend Query operator&(const Query&, const Query&);
AndQuery(Query left, Query right):
BinaryQuery(left, right, "&") { }

// concrete class: AndQuery inherits display and defines remaining pure virtual
std::set<line_no> eval(const TextQuery&) const;
};

class OrQuery: public BinaryQuery {
friend Query operator|(const Query&, const Query&);
OrQuery(Query left, Query right):
BinaryQuery(left, right, "|") { }

// concrete class: OrQuery inherits display and defines remaining pure virtual
std::set<line_no> eval(const TextQuery&) const;
};

inline Query operator&(const Query &lhs, const Query &rhs)
{
return new AndQuery(lhs, rhs);
}
 
Reply With Quote
 
 
 
 
Rui Maciel
Guest
Posts: n/a
 
      01-05-2013
fl wrote:

> Hi,
>
> I am learning C++ with "C++ Primer" 4th edition. There is a class
> derivation example in Chapter 15. When I step through the code, I find
> that the base class destructor ,,,,,,, ~Query() { decr_use(); }
> ,,,,,,,
> executes after the derived class AndQuery() (which is derived from
> BinaryQuery, which is then derived from Query_base) constructor runs:
> ,,,,,,,,,,,,,,,,,, AndQuery(Query left, Query right): BinaryQuery(left,
> right, "&") { } ,,,,,,,,,,,,,,,,,,
>
> The above problem appears when I run
>
> Query orq = Query(sought1) & Query(sought2);
>
> It triggers the "and" operator. I see that it copies "left" to "lhs" etc.:
>
> BinaryQuery(Query left, Query right, std::string op): lhs(left), rhs
> right, oper(op) { }
>
> The next call
> ~Query() { decr_use(); }
> puzzles me a lot.
>
> Could you explain it to me?
>
> Thanks in advance



First of all, you could help others help you by supplying a small example
which can be compiled and executed. You've mentioned that you suspect that
the problem you've experienced is caused by a statement which is nowhere to
be found in your example. This means that if you expect others to help you,
you are also expecting that they both guess what the code is and spend their
time trying to reproduce it.

Regarding your post, you've referred to the following line:

> Query orq = Query(sought1) & Query(sought2);


You've also stated that you aren't sure why a destructor is called.

If you look at the above line, you will notice that it is equivalent to:

Query orq = AndQuery( Query(sought1), Query(sought2) );

In the above statement, you've declared two temporary objects of class
Query, which you pass as parameters to AndQuery(). According to the C++
standard, those temporary objects are destroyed as the last step in
evaluating the full-expression. This means that once that call to
AndQuery() is evaluated, Query::~Query() will be called.

In short, every object has a lifetime. When that lifetime starts the
constructor is called, and when that lifetime ends then the destructor is
called.


Hope this helps,
Rui Maciel
 
Reply With Quote
 
 
 
 
fl
Guest
Posts: n/a
 
      01-06-2013
On Saturday, January 5, 2013 10:27:08 AM UTC-5, Rui Maciel wrote:
> fl wrote:
>
>
>
> > Hi,

>
> >

>
> > I am learning C++ with "C++ Primer" 4th edition. There is a class

>
> > derivation example in Chapter 15. When I step through the code, I find

>
> > that the base class destructor ,,,,,,, ~Query() { decr_use(); }

>
> > ,,,,,,,

>
> > executes after the derived class AndQuery() (which is derived from

>
> > BinaryQuery, which is then derived from Query_base) constructor runs:

>
> > ,,,,,,,,,,,,,,,,,, AndQuery(Query left, Query right): BinaryQuery(left,

>
> > right, "&") { } ,,,,,,,,,,,,,,,,,,

>
> >

>
> > The above problem appears when I run

>
> >

>
> > Query orq = Query(sought1) & Query(sought2);

>
> >

>
> > It triggers the "and" operator. I see that it copies "left" to "lhs" etc.:

>
> >

>
> > BinaryQuery(Query left, Query right, std::string op): lhs(left), rhs

>
> > right, oper(op) { }

>
> >

>
> > The next call

>
> > ~Query() { decr_use(); }

>
> > puzzles me a lot.

>
> >

>
> > Could you explain it to me?

>
> >

>
> > Thanks in advance

>
>
>
>
>
> First of all, you could help others help you by supplying a small example
>
> which can be compiled and executed. You've mentioned that you suspect that
>
> the problem you've experienced is caused by a statement which is nowhere to
>
> be found in your example. This means that if you expect others to help you,
>
> you are also expecting that they both guess what the code is and spend their
>
> time trying to reproduce it.
>
>
>
> Regarding your post, you've referred to the following line:
>
>
>
> > Query orq = Query(sought1) & Query(sought2);

>
>
>
> You've also stated that you aren't sure why a destructor is called.
>
>
>
> If you look at the above line, you will notice that it is equivalent to:
>
>
>
> Query orq = AndQuery( Query(sought1), Query(sought2) );
>
>
>
> In the above statement, you've declared two temporary objects of class
>
> Query, which you pass as parameters to AndQuery(). According to the C++
>
> standard, those temporary objects are destroyed as the last step in
>
> evaluating the full-expression. This means that once that call to
>
> AndQuery() is evaluated, Query::~Query() will be called.
>
>
>
> In short, every object has a lifetime. When that lifetime starts the
>
> constructor is called, and when that lifetime ends then the destructor is
>
> called.
>
>
>
>
>
> Hope this helps,
>
> Rui Maciel


Thanks for your answer. I do like to post the whole project files along with the question. The problem is that there are 7 small files in this project.. And there is either no zip file upload functionality in this group.

I have a further question here. I am using Microsoft Visual C Express Studio to debug this project. When it runs through ~Query(), I do not know whichparameter copy is destroyed. Is there a way or trick to get that information in debugging? i.e. to know the "*this" content?
Thanks.
 
Reply With Quote
 
Tobias Müller
Guest
Posts: n/a
 
      01-06-2013
fl <(E-Mail Removed)> wrote:
> Thanks for your answer. I do like to post the whole project files along
> with the question. The problem is that there are 7 small files in this
> project. And there is either no zip file upload functionality in this group.


That's not a good idea. Most people here are not willing to look through a
complete project.
Try reducing your code to a (working) minimum, s.t. the problem still
occurs. Optimally, you will find the problem by yourself in the process,
but at least you will learn something.

> I have a further question here. I am using Microsoft Visual C Express
> Studio to debug this project. When it runs through ~Query(), I do not
> know which parameter copy is destroyed. Is there a way or trick to get
> that information in debugging? i.e. to know the "*this" content?
> Thanks.


There's a debug window called 'Locals' that shows all local variables and
their values (including 'this').
You can also hover over any variable in the source code to see it's value.

That's off topic here however.

Tobi
 
Reply With Quote
 
Rui Maciel
Guest
Posts: n/a
 
      01-06-2013
fl wrote:

> Thanks for your answer. I do like to post the whole project files along
> with the question. The problem is that there are 7 small files in this
> project. And there is either no zip file upload functionality in this
> group.


You don't need to post your project. You only need to post the smallest
example you can come up with that reproduces the same problem.


> I have a further question here. I am using Microsoft Visual C Express
> Studio to debug this project. When it runs through ~Query(), I do not know
> which parameter copy is destroyed. Is there a way or trick to get that
> information in debugging? i.e. to know the "*this" content? Thanks.


First of all, all temporary copies will be destroyed. The standard states
that, when a function is called, the initialization and destruction of each
parameter occurs within the context of the calling function.

The tricky bit is to know what temporary objects are created, and their
creation order. I don't know if the standard even specifies the order in
which local objects within a block are destroyed, and I suspect it doesn't.
Therefore, if you really want to make sure you know in which order they are
created and destroyed, I suspect that the only option you have is to
allocate and deallocate them them dynamically, which would be an ugly hack.

Then again, why should this matter?


Rui Maciel
 
Reply With Quote
 
Rui Maciel
Guest
Posts: n/a
 
      01-06-2013
Paavo Helde wrote:

> Whenever objects are automatically destructed in C++, this is done in the
> reverse order of their creation (more exactly, in the reverse order of the
> completion of their construction). This holds even if the creation order
> itself is not specified. For temporaries this is covered by [12.2/4] in
> the 2003 standard, for example.



That only tells you the relative order in which temporary objects are
destroyed, not the precise order in which that happens. To be able to tell
this, it's necessary to know a bit more stuff, including the order in which
they were created, which in some cases isn't possible to tell.

The standard states that lifetime of an object starts after its
initialization is complete [3.8]. In this example, when a function is
called, the standard states that the sequence in which the parameters are
initialized is indetermined [5.2.2]. Therefore, if it isn't possible to
tell the order in which objects start their lifetime, it isn't possible to
tell which order their lifetime ends.

The only guarantee that the standard gives is that, no matter in which order
they are created, they are destroyed in the reverse order. But that info
isn't enough to let anyone say precisely which object is destroyed first.


Rui Maciel
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: Why has python3 been created as a seperate language where thereis still python2.7 ? Michiel Overtoom Python 28 06-28-2012 10:14 AM
Re: Why has python3 been created as a seperate language where thereis still python2.7 ? Stefan Behnel Python 3 06-26-2012 10:37 PM
Re: Why has python3 been created as a seperate language where thereis still python2.7 ? Andrew Berg Python 0 06-25-2012 08:48 AM
findcontrol("PlaceHolderPrice") why why why why why why why why why why why Mr. SweatyFinger ASP .Net 2 12-02-2006 03:46 PM
friend ostream& operator <<, compiles and runs in Dev++ no output or error in Borland Rock C++ 4 11-20-2005 06:26 AM



Advertisments