Forgive me if this has already been asked, I didn't find any answers to my specific question.
I have a class in a library I'm making that I want certain classes to be able to create and destroy, and other classes to be able to access other public functions. Having a friend class is not what I want either as the friend class will get access to member variables and member functions which I don't want. I stumbled upon this idiom which almost works, except for the destructor since it can't take additional parameters. With that idiom, I get:
class B;
class A
{
public:
class LifecycleKey
{
private:
LifecycleKey() {}
friend class B;
};
A(LifecycleKey); // Now only class B can call this
// Other public functions
private:
~A(); // But how can I get class B to have access to this?
void somePrivateFunction();
// Members and other private functions
};
As alluded to in the above code, the solution doesn't allow only class B to have access to the destructor.
While none of the above issues are deal breakers by any stretch as I can always just make ctor and dtor public and just say "RTFM".
My question is:
Is there is some way to limit access to ctor and dtor to specific classes (but only the ctor and dtor) while adhering to more well known syntax (having stuff be on the stack if people want, destroying via delete , etc.)?
Any help is greatly appreciated!
SOLUTION
in A.h
class B;
class A
{
protected:
A() {}
virtual ~A() {}
A(const A&); // Implement if needed
A(A&&); // Implement if needed
public:
// Public functions
private:
void somePrivateFunction();
// Members and other private functions
};
in B.h
class B
{
public:
B();
~B();
const A* getA() const;
private:
A* m_a;
}
in B.cpp
namespace {
class DeletableA : public A {
public:
DeletableA() : A() {}
DeletableA(const DeletableA&); // Implement if needed
DeletableA(DeletableA&&); // Implement if needed
~DeletableA() {}
}
}
#include B.h
B::B() : m_a(new DeletableA()) {}
B::~B() { delete static_cast<DeletableA*>(m_a); }
const A* B::getA() const { return m_a; }
Alternatively, if the DeletableA class is needed in B.h or A.h (due to inlining, templating, or desire to have all class A related classes in A.h), it can be moved there with a "pass key" on the constructor so no other classes can create one. Even though the destructor will be exposed, no other class will ever get a DeletableA to delete.
Obviously this solution requires that class B know to make instances of Deletable A (or to make the class in general if it isn't exposed in A.h) and only store A* that are exposed via public functions, but, it is the most flexible set up that was suggested.
While still possible for some other class to make a subclass of class A (since class A isn't "final"), you can add another "pass key" to the constructor of A to prevent such behavior if you wish.
For the goal that class B should be the only one able to instantiate and destroy objects of class A:
For static and automatic variable, restricting access to the constructor is all that's needed, and you're already doing that.
For dynamically allocated object you can restrict access to its deallocation functions, operator delete, and operator delete[], and leave the destructor public. This prohibits other code than B from deleting objects.
For dynamically objects you can derive class A from an interface with protected virtual destructor or named self-destroy function, which has class B as friend. B can then destroy any dynamic A object by casting up to the interface that it has access to.
Code that explicitly calls the destructor deserves whatever it gets.
Remember, you're never building an impregnable defense against malicious code, you're just building a reasonable detection and compile time reporting of inadvertent incorrect use.
Use a mediator-class:
class mediator;
class A
{
/* Only for B via mediator */
A();
~A(); // But how can I get class B to have access to this?
friend class mediator;
/* Past this line the official interface */
public:
void somePrivateFunction();
protected:
private:
};
class B;
class mediator
{
static A* createA() { return new A{}; }
static void destroyA(const A* p) { delete p; }
// Add additional creators and such here
friend class B;
};
Thus, only the mediator, as part of the interface to B, gets full access.
BTW: Instead of restricting access to the dtor, you might get happier overloading new and delete and restricting access to them.
The advantage: Allocation on the stack is generally possible, if the variable is directly initialized without copying.
void* operator new(std::size_t);
void* operator new[](std::size_t);
void operator delete(void*);
void operator delete[](void*);
void operator delete(void*, std::size_t) noexcept;
void operator delete[](void*, std::size_t) noexcept;
use shared_ptr
class K{
public:
int x;
private:
~K(){};
K(){};
private:
friend class K_Creater;
friend class K_Deleter;
};
struct K_Deleter{ void operator()(K* p) { delete p; } };
struct K_Creater{
static shared_ptr<K> Create(){
return shared_ptr<K>(new K, K_Deleter() );
}
};
//K* p = new K; prohibited
shared_ptr<K> p = K_Creator::Create();
another answer is:
#include <iostream>
class A
{
public:
class Key
{
private:
Key(){
std::cout << "Key()" << std::endl;
}
~Key(){
std::cout << "~Key()" << std::endl;
}
friend class B;
};
A(Key key){
std::cout << "A(Key key)" << std::endl;
}
void seti(){ i_=0;}
private:
int i_;
};
class B{
public:
static void foo(){
A a{A::Key()};
A* pa = new A( A::Key() );
delete pa;
static A sa({});
}
};
int main(){
B::foo();
//A a{A::Key()}; prohibit
//A* pa = new A( A::Key() ); prohibit
//delete pa; prohibit
//static A sa({}); prohibit
return 0;
}
My take:
Any class/function that has access to the constructor should also have access to the destructor.
You should make ~A() public since A() is public. Since no other client except B can use the constructor, they won't have the need to use the destructor anyway.
You can further limit who can access the destructor by declaring away the copy and move constructors, and the new and delete operators.
Update
Making the destructor public and declaring away the copy and move constructors seems to address all of your concerns. You don't even need to declare away the new and delete operators or their array variants.
Here's what I think should meet most of your needs.
class B;
class PassKey
{
private:
PassKey() {}
~PassKey() {}
friend class B;
};
class A
{
public:
A(PassKey) {}
~A() {}
private:
// Declare away
A(A const&);
A(A&&);
};
Now, let's take a look at what B can have:
class B
{
public:
B() : a(PassKey()), ap(new A(PassKey())), ap2(new A(PassKey())) {}
~B() { delete ap; }
A const& getA() const {return a;}
A a;
A* ap;
std::shared_ptr<A> ap2;
};
It can have the following member data types:
Objects of type A.
Raw pointers to objects of type A.
Objects of type shared_ptr<A>.
Member functions of B can also create any of the above types of objects.
Other classes can't use objects of type A since they cannot construct one in any way. All the following attempts to use A in various forms fail.
struct C
{
C() : a(PassKey()) {} // Can't construct an instance of A
// since C doesn't have access to
// PassKey's constructor.
A a;
};
struct D
{
D() : a(new A(PassKey())) {} // Can't construct an instance of A
// since D doesn't have access to
// PassKey's constructor.
A* a;
};
struct E
{
E(A const& a) : ap(new A(a)) {} // Can't construct an instance of A
// since E doesn't have access to
// A's copy constructor.
A* ap;
};
class F
{
public:
F(A& a) : ap(new A(std::move(a))) {} // Can't construct an instance of A
// since F doesn't have access to
// A's move constructor.
A* ap;
};
Related
I have a class B which has a member that is a pointer to an object of a A class. When using copy constructor on an object of type A, it is copied but the member variable is not.
Is there any way to copy an A object and to automatically make a copy of its B member?
The following code shows the problem I'm triying to explain:
class A
{
public:
A(char t_name)
{
name = t_name;
}
~A()
{
}
char name;
};
class B
{
public:
A* attribute;
B()
{
attribute = new A('1');
}
~B()
{}
};
int main()
{
B* b_variable = new B;
B* b_copy = new B(*b_variable);
return 0;
}
When using copy constructor on an object of type A, it is copied but the member variable is not.
Your code never calls any copy constructor in class A.
Your code calls a copy constructor in class B and it does exactly what is is supposed to, i.e. copies the value of attribute which is a pointer to a class A object.
In other words - after executing your code, you have two instances of class B and one class A instance. In the two class B instances attribute points to the same class A instance.
This is (most likely) not what you want.
As many already has pointed out (e.g. see #lostbard answer), you'll need a copy constructor in class B to do a deep-copy. A deep-copy is needed because class B have a pointer member.
Also you should do some clean up in class B destructor and in main.
#include <iostream>
using namespace std;
class A
{
public:
A(char t_name)
{
name = t_name;
}
~A()
{
}
char name;
};
class B
{
public:
A* attribute;
B()
{
attribute = new A('1');
}
/** Copy constructor */
B(const B ©)
{
// Make a new instance of class A
attribute = new A(*copy.attribute);
}
/** Assignment operator */
B& operator= (const B& other)
{
// Delete the existing class A instance
delete attribute;
// and create a new as a copy of other.attribute
attribute = new A(*other.attribute);
}
~B()
{
// Delete the class A instance
delete attribute;
}
};
int main()
{
B* b_variable = new B;
B* b_copy = new B(*b_variable);
// Delete the two class B instances
delete b_variable;
delete b_copy;
return 0;
}
There is no need for a copy constructor in class A. The default generated will do as Class A has no pointer members.
EDIT
As pointed out by #Slava you should always implement a assignment operator when you make a copy constructor (rule of three) so I added it to the code above.
Some like the rule of three to be the rule of five so it also include move. Read more here: https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)
While there are many solutions, I'd bet sooner-or-later you'll end up with implementing the usual virtual a.clone();. That works flawless when you'll have derived classes of A (which is more-or-less the only legitimate reason why you're keeping A as a pointer to a heap-allocated object and not as a value member :) ).
Note that, when you're implementing clone() in your hierarchy, that C++ supports covariant pointer return types. Thus, if base has a virtual that returns e.g. Clonable*, then A's same method can return A* and A's descendant ADerived can return ADerived*. Feel free to understand 'can' as 'should' for the case of clone().
Create a copy constructor for A and for B:
class A
{
public:
A(char t_name)
{
name = t_name;
}
A(const A& copy)
{
name = copy.name;
}
~A()
{
}
char name;
};
class B
{
public:
A* attribute;
B()
{
attribute = new A('1');
}
B(const B ©)
{
attribute = new A(*copy.attribute);
}
~B()
{}
};
I've got a class A (from a library over which I have no control) with a private copy constructor and a clone method, and a class B derived from A. I would like to implement clone for B as well.
The naive approach
#include <memory>
class A { // I have no control here
public:
A(int a) {};
std::shared_ptr<A>
clone() const
{
return std::shared_ptr<A>(new A(*this));
}
private:
A(const A & a) {};
};
class B: public A {
public:
B(int data, int extraData):
A(data),
extraData_(extraData)
{
}
std::shared_ptr<B>
clone() const
{
return std::shared_ptr<B>(new B(*this));
}
private:
int extraData_;
};
int main() {
A a(1);
}
however, fails, since the copy constructor of A is private:
main.cpp: In member function ‘std::shared_ptr<B> B::clone() const’:
main.cpp:27:42: error: use of deleted function ‘B::B(const B&)’
return std::shared_ptr<B>(new B(*this));
^
main.cpp:17:7: note: ‘B::B(const B&)’ is implicitly deleted because the default definition would be ill-formed:
class B: public A {
^
main.cpp:14:5: error: ‘A::A(const A&)’ is private
A(const A & a) {};
^
main.cpp:17:7: error: within this context
class B: public A {
There might a way to make use of A::clone() for B::clone(), but I'm not sure how this would work exactly. Any hints?
I presume it's a typo that your B has no public members at all,
and that you're missing a public: before the definition of B::B(int,int).
The author of the class represented by your A apparently wants it to be
cloneable but not copy constructible. That would suggest he or she wants all
instances to live on the heap. But contrariwise, there's the public
constructor A::A(int). Are you sure you are right about that?
It's plausible to suppose that the class can reveal enough information
about a given instance to constitute another instance. E.g., putting
a little more flesh on A:
class A {
public:
A(int a)
: data_(a){};
std::shared_ptr<A>
clone() const
{
return std::shared_ptr<A>(new A(*this));
}
int data() const {
return data_;
}
private:
A(const A & a) {};
int data_;
};
And if that is true, then the public constructor would render it merely
inconvenient to circumvent the private, undefined copy constructor:
A a0(1);
A a1{a0.data()}; // Inconvenient copy construction
So I'm less than confident that A faithfully represents the problem
class. Taking it at face value, however, the question you need to answer
is: Can you even inconveniently copy construct an A?
If not then you're stuck. If so, then you can use inconvenient copy
construction of A to expressly define a conventional copy constructor for B,
which is all you need. E.g.
class B: public A {
public:
B(B const & other)
: A(other.data()),extraData_(other.extraData_){}
B(int data, int extraData):
A(data),
extraData_(extraData)
{
}
std::shared_ptr<B>
clone() const
{
return std::shared_ptr<B>(new B(*this));
}
int extradata() const {
return extraData_;
}
private:
int extraData_;
};
#include <iostream>
int main()
{
B b(1,2);
std::shared_ptr<B> pb = b.clone();
std::cout << pb->data() << std::endl;
std::cout << pb->extradata() << std::endl;
return 0;
}
You need to make the copy-constructor of A protected so that the derived class could use it:
protected:
A(const A & a) { /*...*/ }
Hope that helps.
The reason the default definition of B's copy constructor is ill-formed is because - if it was permitted - it would invoke the private (therefore inaccessible to B) and not defined copy constructor of A.
Make A's copy constructor either protected or public, so it is accessible to B. Another (really bad) option is to declare class B as a friend of A. All possibilities would also require you to provide a definition for A's copy constructor.
I'm curious if in the following program Base* base in class Container can be replaced with Base& base?
With Base base it can't be replaced, because Base is abstract.
With Base& base the object should be allocated somewhere, so I still would not be able to get rid of the pointer to the allocated object.
#include <iostream>
class Base
{ public:
virtual void str()=0;
};
class A : public Base
{ int i;
public:
A(int i):i(i){}
void str(){std::cout<<i<<std::endl;}
};
class B : public Base
{ double f;
public:
B(double f):f(f){}
void str(){std::cout<<f<<std::endl;}
};
class Container
{ Base *base;
public:
Container(int i) { base=new A(i);}
Container(double f) { base=new B(f);}
void str(){ base->str();}
};
int main ()
{
Container c1(8),c2(13.0);
c1.str();
c2.str();
return 0;
}
With your code, I would't recommend it, because Container is the owner of base and a reference, semantically, means something else (an alias).
Technically, there's nothing stopping you:
class Container
{ Base &base;
public:
Container(int i) : base(*new A(i)) {}
Container(double f) : base(*new B(f)) {}
void str(){ base->str();}
};
Note that references have to be initialized in the initializer list.
You'd still need to clean up the memory, and it would look ugly with a reference:
~Container() { delete &base; }
instead of
~Container() { delete base; }
if you used pointers. Of course, using a std::unique_ptr instead of either of these two would make life a whole lot easier.
Also, be sure to implement (or declare as private or deleted) the copy constructor or assignment operator.
Later spot - You need to provide Base with a virtual destructor, otherwise you'll run into undefined behavior territory when you'll attempt to clean up the memory.
I am trying to use a smart pointer class in the following way
class A
{
friend class B;
virtual methods ();
protected:
virtual ~classA();
}
class B:public QSharedPointer<class A>
{
class B();
~ class B();
}
I plan to replace occurrences of Class A* with class B. Is this approach correct?
No this is not really the way to do this. It looks like your design goal here is to make it impossible for someone to allocate an object of type A without putting it in a smart pointer. The normal way to do this is not to inherit from the smart pointer, but to make your type have
A private constructor
A private destructor
A public static factory method returning in this case QSharedPointer
A private deleter class that is a friend of class A
Here is an example using boost::shared_ptr (I do not have a QT installation right now, but you should be able to just replace all instances of boost::shared_ptr with QSharedPointer)
#include <boost/shared_ptr.hpp>
class A {
private:
A() {}
~A() {}
struct deleter {
void operator()(A* val) {delete val;}
};
friend class deleter;
public:
static boost::shared_ptr<A> create() {
return boost::shared_ptr<A>(new A(), A::deleter());
}
};
int main()
{
//A a1; //compile error
//A *a2 = new A(); //compile error
boost::shared_ptr<A> a3 = A::create();
return 0;
}
I get a compile error, which I'm slightly confused about. This is on VS2003.
error C2248: 'A::y' : cannot access protected member declared in class 'A'
class A
{
public:
A() : x(0), y(0) {}
protected:
int x;
int y;
};
class B : public A
{
public:
B() : A(), z(0) {}
B(const A& item) : A(), z(1) { x = item.y;}
private:
int z;
};
The problem is with x = item.y;
The access is specified as protected. Why doesn't the constructor of class B have access to A::y?
It's because of this:
class base_class
{
protected:
virtual void foo() { std::cout << "base::foo()" << std::endl; }
};
class A : public base_class
{
protected:
virtual void foo() { std::cout << "A::foo()" << std::endl; }
};
class B : public base_class
{
protected:
virtual void foo() { std::cout << "B::foo()" << std::endl; }
public:
void bar(base_class *b) { b->foo(); }
};
If that were legal, you could do this:
A a;
B b;
b.bar(&a);
And you'd be calling a protected member of A from B, which isn't allowed.
The other answers explain the reasoning behind preventing your B object from accessing the protected parts of A in your example, even though B 'is-a' A. Of course, the easiest way to fix this problem is to make the parts of A you want access topublic` or have publicly accessible accessor methods.
However you might decide that's inappropriate (or you might not have control over the definition of A). Here are some suggestions to let you work around the problem, in increasing order of subverting A's access control. Note that all of these workarounds assume that class A is copy-constructable.
In the first case, you simply use the copy constructor for A to set up an initial state for that part of the B object, then fix it up afterward:
class B1 : public A
{
public:
B1() : A(), z(0) {}
B1(const A& item) : A(item), z(1) {
// fix up the A sub-object that was copy constructed
// not quite the way we wanted
x = y;
y = 0;
}
private:
int z;
};
I find that incredibly confusing and probably very error prone (assuming that we want the A sub-object in the B object to be different than the A object being passed to the constructor - an unusual situation, but it's what was given in the problem). However, the fact that it can be done gives some justification for the more subversive examples that follow...
The next example creates a temporary B object that has an exact duplicate of the A object we want access to. We can then use the temporary B object to get to the items that were protected:
class B2 : public A
{
public:
B2() : A(), z(0) {}
B2(const A& item) : A(), z(1) {
// create a special-use B2 object that can get to the
// parts of the A object we want access to
B2 tmp( item, internal_use_only);
x = tmp.y; // OK since tmp is of type B
}
private:
int z;
// create a type that only B2 can use as a
// 'marker' to call a special constructor
// whose only purpose in life is to create
// a B object with an exact copy of another
// A sub-object in it
enum internal_use {
internal_use_only
};
B2( const A& item, internal_use marker) : A(item), z(0) {};
};
I find that solution to be a bit less confusing than the first, but it's still confusing (in my opinion). Having a bastard version of of B object just to get to the parts of the A object we want is odd.
We can do something about that by creating a special proxy for A objects that gives the access we want. Note that this is the 'most subversive' workaround because it's something that any class could do to get to protected parts of A, even if they aren't sub-classes of A themselves. In the case of the B class, there's some legitimacy to getting to the protected parts of A objects, since B is-a A, and as we've already seen there are workarounds that let us get access that use only rights that class B already has, so I consider this a cleaner version of those workarounds in class B's case.
class B3 : public A
{
public:
B3() : A(), z(0) {}
B3(const A& item) : A(), z(1) {
// a special proxy for A objects that lets us
// get to the parts of A we're interested in
A_proxy tmp( item);
x = tmp.get_y();
}
private:
int z;
class A_proxy : public A
{
public:
A_proxy( const A& other) : A(other) {};
int get_x() {return x;};
int get_y() {return y;};
};
};
IBM's documentation summarizes it best:
A protected nonstatic base class
member can be accessed by members and
friends of any classes derived from
that base class by using one of the
following:
A pointer to a directly or indirectly derived class
A reference to a directly or indirectly derived class
An object of a directly or indirectly derived class
Thus, using your example above as the basis:
B::B(const A& item) : A(), z(1) {
// NOT OK because `item` is not a reference to the derived class B
//int i = item.y;
// OK because `item` reinterpreted as a reference to the derived class B
// Do not do this (bad!) -- for illustrative purposes only
int i = reinterpret_cast< const B& >(item).y;
// OK because it is equivalent to `this->x = i`,
// where `this` is a pointer to the derived class B
x = i;
}