Fully Answered Interview questions

R

Ron Natalie

Syam said:
Fully answered interview questions in c and c++ from
http://www.faiqs.com
The answers (at least for the first few questions) are in correct.
They aren't the questions I would ask during an interview anyhow.

For instannce question #1
What is the output of printf(“%d”)
?

1. %d helps to read integer data type of a given variable
2. when we write (“%d”, X) compiler will print the value of x
assumed in the main
3. but nothing after (“%d”) so the output will be garbage
4. printf is an overload function doesnt check consistency of the
arg list – segmentation fault


#1 Printf reads NOTHING.
#2 "x assumed in main" is wrong, first, C++ is case dependent. Second,
what X gets printed depends what is defined in the calling scope. Even
if the printf is called form main(), the X may be declared elsewhere.

#3 is likely but not the correct answer.
#4 is beginning to sound correct, but printf is NOT "an overload
functino".

The correct answer is:

1. Printf does not (can not) check the consistency of the arguments.
You must pass the correct number and type arguments to match the
format string.

2. Failure to do so yields undefined behavior (printing garbage and
segmentation faults are just part of the unbounded possibilities
here).
 
E

Earl Purple

(ok, let's try again. I didn't see this was comp.lang.c++.moderated. If
this ends up posting twice, it's because several minutes later this
response has still not appeared)

Fully answered interview questions in c and c++ from
http://www.faiqs.com

Many of those answers are inaccurate or they do not answer the
question.

1. What is the output of printf( "%d" );

The correct answer is given by Ron Natalie: as the function will look
for another argument that doesn't exist, it will cause undefined
behaviour. It might output a random number or it might crash (seg
fault). Unlikely to do anything other than these two though technically
it could.

2. What will happen if I say "delete this" ?

The answer given is a load of nonsense. Some of the statements are
plain false, others simply divert and do not answer the question.

Anyway, it should cause the object to be destructed as though you had
called delete on the pointer from outside. For it to work correctly the
following must be the case:

- The object must have been created with new. That is, regular new.
new(nothrow) would also work but no placement new, no new[].

- If this is a base class and the real object is derived from this, the
destructor must be virtual.

3. Difference between C structure and C++ structure.

Well I will assume they mean C struct and C++ struct as struct is the
keyword not structure. The question does not ask the difference between
a C++ class and a C++ struct.

The real answer is that a C++ struct is effectively a C++ class with
default public access and inheritance. Unlike a C struct you can have
member functions, have private members if you want and have it derive
from base classes (as well as derive classes from it).

There is also a difference in the notation for using one, i.e. you can
simply refer to it by its type without a "struct" prefix or a typedef.

This answer is false:
"members of a struct in C are by default public, in C++ private". It is
false not because C doesn't have a concept of private because
technically it's true, they are by default public even if it is
impossible to change it. But in C++ they are also by default public,
therefore the statement is false.

This answer is off-topic:
"unions like structs except they shaer memory ..." (goes on to give
details on unions).;

4. Difference between assignment operator and copy constructor.

None of the given answers are complete and accurate. This is close.
"copy constructor creates a new object, assignment operator has to deal
with existing data in the object" .

Better answer is "copy constructor creates a new object that clones the
existing one, assignment modifies an existing object to be the clone of
another one". You might add that "although one may overload the copy
constructor and assignment such that the two objects do not actually
match, doing so would be unusual and would be against the normal nature
of what these are supposed to do".

This answer is not quite correct:

"copy constructor always creates a new object, assignment never does".
Because assignment can, it will often create a temporary object to be
exception safe should this action fail.

5. Difference between overloading and overriding.
Let me say this is a horrible question because it is based on one
knowing the technical names for things.

Anyway, overloading is using the same name but a different parameter
list. Overriding uses the same parameter list but where the original
function has been declared virtual in a base class. Overloads are
resolved at compile time, overrides usually at run-time. Overloads can
be particularly useful where one set of code could call either version
according to different circumstances, for example, a template
parameter.

6. Virtual
7. Dynamic binding.

Neither of these are questions. Or does it mean "what are they" in
which case they may as well be asked together, or ask "what is
polymorphism" and perhaps could have been combined with the above
question.

Anyway, the principle is to allow code to call a method by what it does
rather than how it does it, with the actual function that gets called
to be resolved at run-time. The same code that holds a pointer or
reference to a base class could invoke different overloads at different
times, depending on what actual object it has and the type of that
object.

8. Explain the need for a virtual destructor:

The 2nd and 3rd and 5th answers pretty much answer this.

9. Rule of 3.

The first answer answers it best, although the term "user-defined" was
never actually used, but was probably implied. Generally though if it
needs any of the 3 it needs the other two as well.

10. Why do you need a virtual destructor when someone says delete using
a Base ptr thats pointing @ a derived object?

Because the standard says you do.
 
E

Earl Purple

Syam said:
Fully answered interview questions in c and c++ from
http://www.faiqs.com

Many of those answers are inaccurate or they do not answer the
question.

1. What is the output of printf( "%d" );

The correct answer is given by Ron Natalie: as the function will look
for another argument that doesn't exist, it will cause undefined
behaviour. It might output a random number or it might crash (seg
fault). Unlikely to do anything other than these two though technically
it could.

2. What will happen if I say "delete this" ?

The answer given is a load of nonsense. Some of the statements are
plain false, others simply divert and do not answer the question.

Anyway, it should cause the object to be destructed as though you had
called delete on the pointer from outside. For it to work correctly the
following must be the case:

- The object must have been created with new. That is, regular new.
new(nothrow) would also work but no placement new, no new[].

- If this is a base class and the real object is derived from this, the
destructor must be virtual.

3. Difference between C structure and C++ structure.

Well I will assume they mean C struct and C++ struct as struct is the
keyword not structure. The question does not ask the difference between
a C++ class and a C++ struct.

The real answer is that a C++ struct is effectively a C++ class with
default public access and inheritance. Unlike a C struct you can have
member functions, have private members if you want and have it derive
from base classes (as well as derive classes from it).

There is also a difference in the notation for using one, i.e. you can
simply refer to it by its type without a "struct" prefix or a typedef.

This answer is false:
"members of a struct in C are by default public, in C++ private". It is
false not because C doesn't have a concept of private because
technically it's true, they are by default public even if it is
impossible to change it. But in C++ they are also by default public,
therefore the statement is false.

This answer is off-topic:
"unions like structs except they shaer memory ..." (goes on to give
details on unions).;

4. Difference between assignment operator and copy constructor.

None of the given answers are complete and accurate. This is close.
"copy constructor creates a new object, assignment operator has to deal
with existing data in the object" .

Better answer is "copy constructor creates a new object that clones the
existing one, assignment modifies an existing object to be the clone of
another one". You might add that "although one may overload the copy
constructor and assignment such that the two objects do not actually
match, doing so would be unusual and would be against the normal nature
of what these are supposed to do".

This answer is not quite correct:

"copy constructor always creates a new object, assignment never does".
Because assignment can, it will often create a temporary object to be
exception safe should this action fail.

5. Difference between overloading and overriding.
Let me say this is a horrible question because it is based on one
knowing the technical names for things.

Anyway, overloading is using the same name but a different parameter
list. Overriding uses the same parameter list but where the original
function has been declared virtual in a base class. Overloads are
resolved at compile time, overrides usually at run-time. Overloads can
be particularly useful where one set of code could call either version
according to different circumstances, for example, a template
parameter.

6. Virtual
7. Dynamic binding.

Neither of these are questions. Or does it mean "what are they" in
which case they may as well be asked together, or ask "what is
polymorphism" and perhaps could have been combined with the above
question.

Anyway, the principle is to allow code to call a method by what it does
rather than how it does it, with the actual function that gets called
to be resolved at run-time. The same code that holds a pointer or
reference to a base class could invoke different overloads at different
times, depending on what actual object it has and the type of that
object.

8. Explain the need for a virtual destructor:

The 2nd and 3rd and 5th answers pretty much answer this.

9. Rule of 3.

The first answer answers it best, although the term "user-defined" was
never actually used, but was probably implied. Generally though if it
needs any of the 3 it needs the other two as well.

10. Why do you need a virtual destructor when someone says delete using
a Base ptr thats pointing @ a derived object?

Because the standard says you do.
 
N

Noah Roberts

Syam said:
Fully answered interview questions in c and c++ from
http://www.faiqs.com

I think that site must be designed to make the dumbasses look
dumbasser. ;)

The answers are long, jumbled strings of nonsense and when you can
derive some statement from them it is wrong.

I say leave it be...those that read it will become obvious in an
interview even faster than they would before making it easier for
people like me, who are honest about their experience and abilities to
the best of their ability, and for those of us that have to do
interviews.
 
M

Marcus Kwok

Earl Purple said:
9. Rule of 3.

The first answer answers it best, although the term "user-defined" was
never actually used, but was probably implied. Generally though if it
needs any of the 3 it needs the other two as well.

I haven't actually checked out the interview question site, but this
article explains why in modern code, one of the 3 is usually a
non-issue, resulting in a "rule of 2":

http://www.artima.com/cppsource/bigtwo.html
 
K

Kai-Uwe Bux

Marcus said:
I haven't actually checked out the interview question site, but this
article explains why in modern code, one of the 3 is usually a
non-issue, resulting in a "rule of 2":

http://www.artima.com/cppsource/bigtwo.html

Hm, interesting piece, but I fail to see the point. They suggest

class Example {
RAII<SomeResource> p_;
RAII<SomeResource> p2_;
public:
Example() :
p_(new SomeResource()),
p2_(new SomeResource()) {}

Example(const Example& other)
: p_(new SomeResource(*other.p_)),
p2_(new SomeResource(*other.p2_)) {}

Example& operator=(const Example& other) {
// Self assignment?
if (this==&other)
return *this;

*p_=*other.p_;
*p2_=*other.p2_;
return *this;
}

~Example() {
std::cout << "Deleting Example, freeing SomeResource!\n";
}
};

where RAII is a variation of auto_ptr<>:

template <typename T> class RAII {
T* p_;
public:
explicit RAII(T* p) : p_(p) {}

~RAII() {
delete p_;
}

void reset(T* p) {
delete p_;
p_=p;
}

T* get() const {
return p_;
}

T& operator*() const {
return *p_;
}

void swap(RAII& other) {
std::swap(p_,other.p_);
}

private:
RAII(const RAII& other);
RAII& operator=(const RAII& other);
};

So far, I am with them. I can see how their Example class does not need a
destructor since that is taken care of by RAII. What I don't see is how
their Example class differs from:

class Example {
SomeResource p_;
SomeResource p2_;
public:
// compiler provided constructors suffice
~Example() {
std::cout << "Deleting Example, no need to free anything!\n";
}
};

After all, in their assignment operator, they do

*p_=*other.p_;
*p2_=*other.p2_;

which rules out a polymorphic use of the pointers since the assignment would
slice. Thus, the proposed solution can only replace non-polymorphic uses of
pointers and for those offers deep copy semantics. That seems to be
pointless. What is it that I am missing?


Best

Kai-Uwe Bux
 
R

Ron Natalie

Marcus said:
I haven't actually checked out the interview question site, but this
article explains why in modern code, one of the 3 is usually a
non-issue, resulting in a "rule of 2":

http://www.artima.com/cppsource/bigtwo.html
Frankly, I've got a number of problems with the article. The fact
that they make an owned pointer class to destruct the thing as
an argument that there is only a RULE of 2 is particularly spurious.

If you are going to make a smart pointer class to handle destruction
why not give it COPY SEMANTICS. Then you can get rid of the
copy constructor and assignment operator as well. With well
designed classes it's usually THREE or NOTHING.
 
E

Earl Purple

Kai-Uwe Bux said:
Hm, interesting piece, but I fail to see the point. They suggest

class Example {
RAII<SomeResource> p_;
RAII<SomeResource> p2_;
public:
Example() :
p_(new SomeResource()),
p2_(new SomeResource()) {}

Example(const Example& other)
: p_(new SomeResource(*other.p_)),
p2_(new SomeResource(*other.p2_)) {}

Example& operator=(const Example& other) {
// Self assignment?
if (this==&other)
return *this;

*p_=*other.p_;
*p2_=*other.p2_;
return *this;
}

~Example() {
std::cout << "Deleting Example, freeing SomeResource!\n";
}
};

where RAII is a variation of auto_ptr<>:

template <typename T> class RAII {
T* p_;
public:
explicit RAII(T* p) : p_(p) {}

~RAII() {
delete p_;
}

void reset(T* p) {
delete p_;
p_=p;
}

T* get() const {
return p_;
}

T& operator*() const {
return *p_;
}

void swap(RAII& other) {
std::swap(p_,other.p_);
}

private:
RAII(const RAII& other);
RAII& operator=(const RAII& other);
};

So far, I am with them. I can see how their Example class does not need a
destructor since that is taken care of by RAII. What I don't see is how
their Example class differs from:

class Example {
SomeResource p_;
SomeResource p2_;
public:
// compiler provided constructors suffice
~Example() {
std::cout << "Deleting Example, no need to free anything!\n";
}
};

After all, in their assignment operator, they do

*p_=*other.p_;
*p2_=*other.p2_;

which rules out a polymorphic use of the pointers since the assignment would
slice. Thus, the proposed solution can only replace non-polymorphic uses of
pointers and for those offers deep copy semantics. That seems to be
pointless. What is it that I am missing?

You are missing the point that when Example has two instances of
SomeResource, then that class must be complete at the point of the
header file, whereas if it is contained in a smart pointer then it can
possibly be incomplete at the class declaration level which can be an
advantage in real life where it is in a separate header file.

There is also no guarantee at the header level that the class
SomeResource is copyable and assignable, but the implementation here
takes advantage of the fact that it is. Otherwise your copy constructor
could be implemented something like other.reset( p->clone() ), which
might allow polymorphic classes too.
 
K

Kai-Uwe Bux

Earl said:
You are missing the point that when Example has two instances of
SomeResource, then that class must be complete at the point of the
header file, whereas if it is contained in a smart pointer then it can
possibly be incomplete at the class declaration level which can be an
advantage in real life where it is in a separate header file.

Hm, in that case, this RAII class should just support copy construction and
assignment. Then it would effectively be a wrapper to complete incomplete
types. I have a simple completion<> smart pointer template (copy semantics,
no support for polymorphism, guaranteed existence of a pointee, forwarding
of comparison operators) in my library for exactly that purpose. Since it
supports assignment and copy construction, the client class usually needs
none of the Big Three.
There is also no guarantee at the header level that the class
SomeResource is copyable and assignable, but the implementation here
takes advantage of the fact that it is. Otherwise your copy constructor
could be implemented something like other.reset( p->clone() ), which
might allow polymorphic classes too.

For both of those cases, I would just use a run-of-the-shelve smart pointer
like shared_ptr or a copy_ptr. Then again, the compiler generated copy
assignment operator, copy constructor, and destructor would be fine:

class Example {
copy_ptr< SomeResource > p_;
copy_ptr< SomeResource > p2_;
public:
// compiler provided constructors suffice
~Example() {
std::cout << "Deleting Example, no need to free anything!\n";
}
};

I still don't see how the Rule of Three turns into a Rule of Two. To me,
that appears to be an artifact of the wicked design of their RAII class.


Best

Kai-Uwe Bux
 
E

Earl Purple

Kai-Uwe Bux wrote:

For both of those cases, I would just use a run-of-the-shelve smart pointer
like shared_ptr or a copy_ptr. Then again, the compiler generated copy
assignment operator, copy constructor, and destructor would be fine:

class Example {
copy_ptr< SomeResource > p_;
copy_ptr< SomeResource > p2_;
public:
// compiler provided constructors suffice
~Example() {
std::cout << "Deleting Example, no need to free anything!\n";
}
};

I still don't see how the Rule of Three turns into a Rule of Two. To me,
that appears to be an artifact of the wicked design of their RAII class.

It could be boost::scoped_ptr. You might want to declare the destructor
and implement it empty but that's not really the same as overloading
it.

#include <boost/scoped_ptr.hpp>
class SomeResource;

class Example
{
boost::scoped_ptr< SomeResource > p_;
boost::scoped_ptr< SomeResource > p2_;

public:
~Example(); // will be implemented empty
Example ( const Example & );
Example & operator=( const Example & );

// rest of public interface
};

// does deep copy with clone()
// Of course SomeResource is complete at this point.

Example::Example( const Example & x )
: p_( x.p_->clone() ),
p2_( x.p2_->clone() )
{
}

// exception-safe assign in case x.p2_->clone() throws

Example & Example::eek:perator=( const Example & x )
{
Example temp( x );
p_.swap( temp.p_ );
p2_.swap( temp.p2_ );
return *this;
}

Example::~Example()
{
// no need to implement this
}

The only reason that I did put ~Example into the implementation is to
ensure the completeness of SomeResource at the time of its destruction.

Of course, you could use a cloning smart-pointer instead of scoped_ptr,
but the example shows where you might have to implement copy and assign
but not destruction.
 
E

Earl Purple

Ron said:
Frankly, I've got a number of problems with the article. The fact
that they make an owned pointer class to destruct the thing as
an argument that there is only a RULE of 2 is particularly spurious.

If you are going to make a smart pointer class to handle destruction
why not give it COPY SEMANTICS. Then you can get rid of the
copy constructor and assignment operator as well. With well
designed classes it's usually THREE or NOTHING.

Show me how you would implement clone_ptr< T > that will work fully
when T is incomplete?. Note that although it requires T to be complete
on destruction, you can get around that by declaring the destructor in
your class then giving it an empty implementation. I don't know any way
of doing that with copy-construct and assign.

Of course you might decide that even declaring it in the class and
giving it empty implementation still counts as "overriding" thus rule
of 3 not 2.

Plus the fact that not everyone is clever enough to write clone_ptr and
there isn't one I know of in boost, although I think Loki may have one.
(Axter, we don't need yours).
 
K

Kai-Uwe Bux

Earl said:
Show me how you would implement clone_ptr< T > that will work fully
when T is incomplete?.

I am willing to take the challenge. However, I am not certain I understand
the requirement "works fully when T is incomplete". Could you provide a
test case that you want to compile and produce specified output so that I
can see whether I am meeting your requirements before posting a solution?
Note that although it requires T to be complete
on destruction, you can get around that by declaring the destructor in
your class then giving it an empty implementation. I don't know any way
of doing that with copy-construct and assign.

Huh? What does "it" refer to: the clone_ptr or T?

[snip]


Best

Kai-Uwe Bux
 
E

Earl Purple

Kai-Uwe Bux said:
I am willing to take the challenge. However, I am not certain I understand
the requirement "works fully when T is incomplete". Could you provide a
test case that you want to compile and produce specified output so that I
can see whether I am meeting your requirements before posting a solution?


Huh? What does "it" refer to: the clone_ptr or T?

The clone_ptr will be complete. T will not be.

This example should work:

// outer.hpp

class Inner;

#include "clone_ptr.hpp"

class Outer
{
clone_ptr< Inner > inner;

public:
explicit Outer( int init = 0 );
int get_value() const;
void set_value( int x );

~Outer(); // must be implemented empty
};


// main source file:

#include "outer.hpp"
#include <iostream>

int main()
{
Outer o1( 10 );
Outer o2( o1 );
Outer o3;
o3 = o2; // test assignment
Outer o4( o3 );
o3.set_value( 20 ); // to check we are modifying only a clone

std::cout << "o1=" << o1.get_value() << ", o2=" << o2.get_value()
<< ", o3=" << o3.get_value() << ", o4=" << o4.get_value()
<< '\n';
}

- You should implement the Inner class, outer.cpp and clone_ptr.hpp
- It should output o1=10, o2=10, o3=20, o4=10
- You may not add any more members to the class Outer.

You should also be able to get it to work with 3 different examples of
Inner:
- 1. Where it uses regular copy-construction.
- 2. Where it uses a clone method and Inner is the actual class
- 3. Where it uses a clone method and the actual class created derives
from Inner.

(I will allow you to put clone_ptr into a namespace if you want but
that is the only change you may make)
 
E

Earl Purple

Earl said:
The clone_ptr will be complete. T will not be.

Thinking about it I could probably take up the challenge too. Don't
think it would be the sort of thing most people could do.

My guess is that you would have an abstract class called a cloner and
the complete version on clone_ptr would use its abstract class to do
the cloning in assignment and copying. The cloner would probably also
clone itself at the same time.

There would be a template constructor into which you can pass a some
cloning functor. It would not necessarily be derived from the cloner
interface, but the constructor would be able to create a cloner
implementation from it, a bit like the way it does with the deleter
now.

I guess we could have a default one in case no parameter is passed in.
This would call the copy-constructor. We could also have one that takes
a member function something like:

clone_ptr( T * ptr, &T::clone() )

or whatever. (Perhaps the member function doesn't have to be from the
same class as the pointer).

Now, are we ready to implement all this and submit it to boost?
 
K

Kai-Uwe Bux

Earl said:
The clone_ptr will be complete. T will not be.

This example should work:

// outer.hpp

class Inner;

#include "clone_ptr.hpp"

class Outer
{
clone_ptr< Inner > inner;

public:
explicit Outer( int init = 0 );
int get_value() const;
void set_value( int x );

~Outer(); // must be implemented empty
};


// main source file:

#include "outer.hpp"
#include <iostream>

int main()
{
Outer o1( 10 );
Outer o2( o1 );
Outer o3;
o3 = o2; // test assignment
Outer o4( o3 );
o3.set_value( 20 ); // to check we are modifying only a clone

std::cout << "o1=" << o1.get_value() << ", o2=" << o2.get_value()
<< ", o3=" << o3.get_value() << ", o4=" << o4.get_value()
<< '\n';
}

- You should implement the Inner class, outer.cpp and clone_ptr.hpp
- It should output o1=10, o2=10, o3=20, o4=10
- You may not add any more members to the class Outer.

You should also be able to get it to work with 3 different examples of
Inner:
- 1. Where it uses regular copy-construction.
- 2. Where it uses a clone method and Inner is the actual class
- 3. Where it uses a clone method and the actual class created derives
from Inner.

(I will allow you to put clone_ptr into a namespace if you want but
that is the only change you may make)

Thanks.


Here is a proof of concept:


// clone_ptr.hpp

#include <algorithm> // swap

template < typename T >
void default_delete ( T* p ) {
delete ( p );
}

template < typename T >
T* default_clone ( T* p ) {
return ( p->clone() );
}

template < typename T >
class clone_ptr {
public:

typedef T value_type;
typedef value_type * pointer;
typedef value_type const * const_pointer;
typedef value_type & reference;
typedef value_type const & const_reference;

private:

typedef void (*deleter) ( pointer );
typedef pointer (*cloner) ( pointer );

cloner the_cln;
deleter the_del;
pointer the_ptr;

public:

friend
void swap ( clone_ptr & a, clone_ptr & b ) {
std::swap( a.the_cln, b.the_cln );
std::swap( a.the_del, b.the_del );
std::swap( a.the_ptr, b.the_ptr );
}

explicit
clone_ptr ( pointer ptr )
: the_cln ( &default_clone<value_type> )
, the_del ( &default_delete<value_type> )
, the_ptr ( ptr )
{}

clone_ptr ( clone_ptr const & other )
: the_cln ( other.the_cln )
, the_del ( other.the_del )
, the_ptr ( other.the_cln( other.the_ptr ) )
{}

clone_ptr & operator= ( clone_ptr const & other ) {
clone_ptr dummy ( other );
swap( dummy, *this );
return ( *this );
}

~clone_ptr ( void ) {
the_del( the_ptr );
}

pointer operator-> ( void ) {
return ( the_ptr );
}

const_pointer operator-> ( void ) const {
return ( the_ptr );
}

reference operator* ( void ) {
return ( *the_ptr );
}

const_reference operator* ( void ) const{
return ( *the_ptr );
}

};

// end of file


// inner.hpp

class Inner {

int i;

public:

Inner ( int init )
: i ( init )
{}

int const & item ( void ) const;
int & item ( void );

virtual
Inner* clone ( void ) const;

};

// end of file


// inner.cpp

#include "inner.hpp"

int const & Inner::item ( void ) const {
return ( i );
}

int & Inner::item ( void ) {
return ( i );
}

Inner* Inner::clone ( void ) const {
return ( new Inner ( i ) );
}

// end of file


// outer.hpp

class Inner;

#include "clone_ptr.hpp"

class Outer
{
clone_ptr< Inner > inner;

public:
explicit Outer( int init = 0 );
int get_value() const;
void set_value( int x );

~Outer(); // must be implemented empty
};

// end of file


// outer.cpp

#include "outer.hpp"
#include "inner.hpp"

Outer::Outer( int init )
: inner ( new Inner ( init ) )
{}

int Outer::get_value() const {
return ( inner->item() );
return ( 0 );
}

void Outer::set_value( int x ) {
inner->item() = x;
}

Outer::~Outer() {}

// end of file


// main.cpp

#include "outer.hpp"
#include <iostream>

int main()
{
Outer o1( 10 );
Outer o2( o1 );
Outer o3;
o3 = o2; // test assignment
Outer o4( o3 );
o3.set_value( 20 ); // to check we are modifying only a clone

std::cout << "o1=" << o1.get_value() << ", o2=" << o2.get_value()
<< ", o3=" << o3.get_value() << ", o4=" << o4.get_value()
<< '\n';
}

// end of file
g++ -g -c -o inner.o inner.cpp
g++ -g -c -o outer.o outer.cpp
g++ -g main.cpp outer.o inner.o
a.out
o1=10, o2=10, o3=20, o4=10


The above assumes that inner has a method clone(). I think, if clone() is
virtual, the above should also work when you use a class derived from
inner. I have, however, not tested that.

If you want to use the copy constructor from inner, just use:

template < typename T >
T* default_clone ( T* p ) {
return ( new T ( *p ) );
}

If you want to make that work with derived classes, you need a templated
constructor like shared_ptr:


template < typename D >
explicit
clone_ptr ( D* ptr )
: the_cln ( &default_clone<D> )
, the_del ( &default_delete<D> )
, the_ptr ( ptr )
{}

Again, I have not tested that, yet.


Best

Kai-Uwe Bux
 
E

Earl Purple

Kai-Uwe Bux said:
template < typename T >
T* default_clone ( T* p ) {
return ( p->clone() );
}


explicit
clone_ptr ( pointer ptr )
: the_cln ( &default_clone<value_type> )
, the_del ( &default_delete<value_type> )
, the_ptr ( ptr )
{}
The above assumes that inner has a method clone(). I think, if clone() is
virtual, the above should also work when you use a class derived from
inner. I have, however, not tested that.

If you want to use the copy constructor from inner, just use:

template < typename T >
T* default_clone ( T* p ) {
return ( new T ( *p ) );
}

If you want to make that work with derived classes, you need a templated
constructor like shared_ptr:


template < typename D >
explicit
clone_ptr ( D* ptr )
: the_cln ( &default_clone<D> )
, the_del ( &default_delete<D> )
, the_ptr ( ptr )
{}

Again, I have not tested that, yet.

I would have made the cloner a settable parameter too. I would provide
one by default with the option of having others too. If you're very
clever then you work out whether or not the class has a clone() method.
If it does you use that as the default cloner, if not you use the copy
constructor. Again this would just be the default, so it would be
possible to specify a different cloner, but that would be done by the
constructor.

Your implementation of clone_ptr has not quite passed the test as in
its present state it is intrusive to the way its object clones. I know
we can fix that.

Do you think, by the way, it would be a useful addition to boost?
 
K

Kai-Uwe Bux

Earl said:
I would have made the cloner a settable parameter too. I would provide
one by default with the option of having others too.
Sure.

If you're very clever then you work out whether or not the class has a
clone() method. If it does you use that as the default cloner, if not you
use the copy constructor.


I think, that's a tough one: I know template magic to figure out whether a
type has a clone method. However, I doubt that it works on incomplete
types. For those, the copy constructor would be selected even when a clone
method is available for the complete type.

Again this would just be the default, so it would be
possible to specify a different cloner, but that would be done by the
constructor.

Your implementation of clone_ptr has not quite passed the test as in
its present state it is intrusive to the way its object clones.
I know we can fix that.

What do you mean by "intrusive"?
Do you think, by the way, it would be a useful addition to boost?

Maybe. Here is how I would provide for custom-clone objects and deleters:

// clone_ptr.hpp

#include <algorithm> // swap
#include <tr1/functional>

template < typename T >
void default_delete ( T* p ) {
delete ( p );
}

template < typename T >
T* default_clone ( T* p ) {
return ( new T (*p) );
}

template < typename T >
class clone_ptr {
public:

typedef T value_type;
typedef value_type * pointer;
typedef value_type const * const_pointer;
typedef value_type & reference;
typedef value_type const & const_reference;

private:

typedef void (delete_signature) ( pointer );
typedef pointer (clone_signature) ( pointer );
typedef std::tr1::function<delete_signature> deleter;
typedef std::tr1::function<clone_signature> cloner;

cloner the_cln;
deleter the_del;
pointer the_ptr;

public:

friend
void swap ( clone_ptr & a, clone_ptr & b ) {
std::swap( a.the_cln, b.the_cln );
std::swap( a.the_del, b.the_del );
std::swap( a.the_ptr, b.the_ptr );
}

explicit
clone_ptr ( pointer ptr )
: the_cln ( &default_clone<value_type> )
, the_del ( &default_delete<value_type> )
, the_ptr ( ptr )
{}

template < typename Cloner >
explicit
clone_ptr ( pointer ptr, Cloner c )
: the_cln ( c )
, the_del ( &default_delete<value_type> )
, the_ptr ( ptr )
{}

template < typename Cloner, typename Deleter >
explicit
clone_ptr ( pointer ptr, Cloner c, Deleter d )
: the_cln ( c )
, the_del ( d )
, the_ptr ( ptr )
{}

template < typename D >
explicit
clone_ptr ( D* ptr )
: the_cln ( &default_clone<D> )
, the_del ( &default_delete<D> )
, the_ptr ( ptr )
{}

template < typename D, typename Cloner >
explicit
clone_ptr ( D* ptr, Cloner c )
: the_cln ( c )
, the_del ( &default_delete<value_type> )
, the_ptr ( ptr )
{}

template < typename D, typename Cloner, typename Deleter >
explicit
clone_ptr ( D* ptr, Cloner c, Deleter d )
: the_cln ( c )
, the_del ( d )
, the_ptr ( ptr )
{}

clone_ptr ( clone_ptr const & other )
: the_cln ( other.the_cln )
, the_del ( other.the_del )
, the_ptr ( other.the_cln( other.the_ptr ) )
{}

clone_ptr & operator= ( clone_ptr const & other ) {
clone_ptr dummy ( other );
swap( dummy, *this );
return ( *this );
}

~clone_ptr ( void ) {
the_del( the_ptr );
}

pointer operator-> ( void ) {
return ( the_ptr );
}

const_pointer operator-> ( void ) const {
return ( the_ptr );
}

reference operator* ( void ) {
return ( *the_ptr );
}

const_reference operator* ( void ) const{
return ( *the_ptr );
}

};

// end of file
 
E

Earl Purple

Kai-Uwe Bux said:
I think, that's a tough one: I know template magic to figure out whether a
type has a clone method. However, I doubt that it works on incomplete
types. For those, the copy constructor would be selected even when a clone
method is available for the complete type.

Yes I was thinking that at the time the pointer is attached to it, that
the pointer type must be complete. But there is no real reason why it
should be.
What do you mean by "intrusive"?

That in this case it forces the templated class to have a clone()
method. Forcing a policy on the templated type isn't always intrusive,
for example, you might say that having a default constructor, an
accessible destructor, being copyable or being assignable are not
intrusive policies, but anything else would probably be considered as
such.
Maybe. Here is how I would provide for custom-clone objects and deleters:

Its usefulness would depend on whether it really is needed in
production code. I notice that your code does not allow a conversion
from clone_ptr< Derived > to clone_ptr< Base > but that was not a
requirement and I'm not sure it should be one. Doing so might require
your cloner and deleter to be a base class so that you can call the
clone() and dispose() methods on them without having to pass in a
pointer.

Of course for that to work, it would have to use the virtual clone()
method model to clone the object, not call its copy-constructor which
would slice, however there is already a slicing problem on a base class
pointer which can be overcome by using a protected copy-constructor and
assign thus enforcing clone() to be called. (If the base is abstract of
course copy-construction wouldn't work anyway although assignment could
still slice).

In this case our cloner would have to clone itself as well as its
pointer assuming we don't want to introduce reference-counting into our
clone_ptr.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top