Reputation: 33
Defining the classes A with private constructor and destructor (it should be so!) and B as a friend class, how can I creat a vector of A objects in B and fill it with the function addA(). I got the error "error C2248: "A::~A": No access to private members whose declaration was made in the A class".
class A
{
private:
A();
A(const std::string& name, const float& num);
~A();
public:
friend class B;
private:
std::string name_;
float num_;
};
A::A()
{
name_ = "NoName";
num_ = 0.0;
}
A::A(const std::string& name, const float& num)
{
name_ = name;
num_ = num;
}
A::~A()
{
}
class B
{
public:
B();
~B();
void addA(const std::string name, const float num);
private:
vector<A> vecA;
};
B::B()
{
}
B::~B()
{
}
void B::addA(const std::string name, const float num)
{
A a(name, num);
vecA.push_back(a);
}
int main()
{
B b;
b.addA("Name", 1.0);
return 0;
}
Upvotes: 1
Views: 887
Reputation: 890
Contrary to what the other answers say, it is possible to do this without any extra indirection.
std::vector
doesn't directly call the constructor and the destructor, but uses an allocator. If you want an std::vector
to manage A objects, you just need to provide it an allocator that implements the construct
and destroy
functions, and that is either a friend of A or a nested class of B (since B is already a friend of A).
Example:
#include <memory>
#include <utility>
#include <vector>
class A {
A() = default;
~A() = default;
friend class B;
};
class B {
template<typename T>
struct custom_alloc : std::allocator<T> {
template<typename U, typename... Args>
void construct(U* p, Args&&... args){
::new(const_cast<void*>(static_cast<const volatile void*>(p))) U(std::forward<Args>(args)...);
}
template<typename U>
void destroy(U* p){
if constexpr (std::is_array_v<U>){
for(auto& elem : *p){
(destroy)(std::addressof(elem));
}
} else {
p->~U();
}
}
};
public:
std::vector<A,custom_alloc<A>> vec;
void new_A(){
vec.push_back(A());
}
};
For the implementation of construct
and destroy
, I used an equivalent implementation of the c++20 versions of std::destroy_at
and std::construct_at
. I suspect that destroy
is overkill and just a call to the destructor would be sufficient, but I'm not sure.
Upvotes: 0
Reputation: 7010
While @Fureeish has a neat solution, here's a slightly simpler alternative: just wrap it.
class AccessPrivate;
class PrivStuff
{
private:
PrivStuff() {}
~PrivStuff() {}
public:
friend class AccessPrivate;
std::string m_data{};
};
class AccessPrivate
{
public:
AccessPrivate() = default;
~AccessPrivate() = default;
PrivStuff m_priv;
};
int main(int argc, char* argv[])
{
std::vector<AccessPrivate> myvec;
myvec.resize(4);
for (auto& stuff : myvec)
{
stuff.m_priv.m_data = "heya";
}
}
If you need something more complicated, like passing in arguments, just add an equivalent constructor to AccessPrivate
and there you go. You can essentially treat AccessPrivate
almost like the actual private class, just one level of indirection.
Upvotes: 1
Reputation: 13434
how can I create a vector of A objects in B [...] ?
You can't do that. While B
is a friend
of A
, std::vector
is not a friend
of A
, which means that it cannot access private
members of A
, e.g., constructor, which is required for a vector to work.
However, if you are okay with a little indirection, little potential performance hit and a change in your signature, you can replace the not-working std::vector<A>
with a workig std::vector<std::unique_ptr<A, deleter>>
.
It's important to note that plain std::unique_ptr
will not work here. It has a similar problem to std::vector
- it cannot access private
destructor of A
. One way to work around it is to outsource the job of constructing and destructing of A
s entirely to B
- via explicit construction and destruction, that is:
new A(name, num)
static void deleter_a(A* a) { delete a; }
in B
's scope.
Now we can do:
std::vector<std::unique_ptr<A, std::function<void(A*)>>> vecA;
instead of: std::vector<A>
or std::vector<std::unique_ptr<A>>
. This is important - neither std::unique_ptr
nor std::vector
construct or destruct your A
s. B
is entirely responsible for constructing (new A(name, num)
) and destructing (static void deleter_a(A* a) { delete a; }
) A
s.
Full B
class:
class B {
public:
B() {}; // or = default
~B() {}; // or = default
void addA(const std::string name, const float num);
private:
static void deleter_a(A* a) { delete a; }
using deleter_a_t = void(A*);
std::vector<std::unique_ptr<A, std::function<deleter_a_t>>> vecA;
};
void B::addA(const std::string name, const float num) {
vecA.push_back(std::unique_ptr<A, std::function<deleter_a_t>>{
new A(name, num), std::function<deleter_a_t>{deleter_a}
});
}
Upvotes: 0