Vector of class object with private constructor and destructor? - c++

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;
}

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.

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 As 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 As. B is entirely responsible for constructing (new A(name, num)) and destructing (static void deleter_a(A* a) { delete a; }) As.
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}
});
}

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.

Related

C++ Base class initialize with member of derived class gives warning "uninitialized field"

I use a base class (A) that manages some data but without having the storage. The derived class (B) has a storage member and initializes the base class (A) with a pointer to that storage and the size of them.
The code model (clang) in the IDE gives me a warning "Field mStorage is uninitialized when used here" at line
explicit B() : A(mStorage.data(), 10) {}
Question 1: Is this a problem as long as I do not use the storage in the base class constructor?
Question 2: If this doesn't cause a problem, is there a way to avoid this warning?
class A
{
public:
explicit A(int* p, size_t s)
: mPtr(p), mSize(s)
{}
void append(int i) { /* ... */ }
private:
int* mPtr = nullptr;
size_t mSize = 0;
};
template <size_t N>
class B : public A
{
public:
explicit B() : A(mStorage.data(), N) {}
private:
std::array<int, N> mStorage {};
};
Update:
add template <size_t N> to class B
My intension is to decouple the normal usage of the class and the template size in class B
void worker_function(const A& a)
{
a.append(int(1));
}
// and also
struct Foo
{
Foo(const A& a) : m_a(a) {}
void do_some_work()
{
a.append(int(1));
}
const A& m_a;
};
void main()
{
B<10> b;
worker_function(b);
// and also
Foo foo(b);
foo.do_some_work();
}
This might work as you intend, but compiler warnings should not be ignored. mStorage doesn't exist yet at the time A is constructed. The base class is constructed first. Maybe the compiler, looking at mStorage, will peek ahead, so to speak, but that's not the required sequence. mStorage is probably just random garbage.
From the skeleton, it's hard to guess what the intent is, but you could easily solve the problem by making a virtual function that returns a pointer to the storage in the derived class. You may also find some sort of template solution.
I suggest you tell us a little more about why you want to design a class this way.
UPDATE:
C++ is unhappy not knowing what N is for a good reason. What happens when worker_function is called for the (N+1)st time? 💥
I can think of two safer approaches. One is to make append a (pure?) virtual function. It won't be easy to write A::append anyway, not to mention avoiding a range error. The other is simply to use std::vector instead of std::array in class A and not try this fancy derivation scheme.
You could use composition instead of inheritance and put the classes in the right order.
Note that I had to do some fiddeling with your constness to get it to actually work.
Example online here.
#include <array>
#include <cstddef>
class A
{
public:
explicit A(int* p, size_t s)
: mPtr(p), mSize(s)
{}
void append(int i) { mPtr[i] = i; ++i; } // My guess
private:
int* mPtr = nullptr;
size_t mSize = 0;
};
template <size_t N>
class B
{
public:
explicit B() : m_a(mStorage.data(), N) {}
operator A&() {
return m_a;
}
private:
std::array<int, N> mStorage {};
A m_a;
};
void worker_function(A& a)
{
a.append(int(1));
}
// and also
struct Foo
{
Foo(A& a) : m_a(a) {}
void do_some_work()
{
m_a.append(int(1));
}
A& m_a;
};
int main()
{
B<10> b;
worker_function(b);
// and also
Foo foo(b);
foo.do_some_work();
}

Inheriting a private constructor

Trying to allow make_unique on a class with private ctor I came into the following strange difference between two cases:
Case 1 - empty ctor - compiles
class A {
int _i;
A(): _i(7) {}
public:
template<typename... T>
static std::unique_ptr<A> create(T&&... t) {
struct enablePrivateCtor : public A {
using A::A;
};
return std::make_unique<enablePrivateCtor>(std::forward<T>(t)...);
}
void doIt() const {
std::cout << _i << std::endl;
}
};
int main() {
auto a = A::create();
a->doIt();
}
Output:
7
Case 2 - non-empty ctor - doesn't compile
class A {
int _i;
A(int i): _i(i) {} // <- change 1, ctor getting int
public:
// no change here!
template<typename... T>
static std::unique_ptr<A> create(T&&... t) {
struct enablePrivateCtor : public A {
using A::A;
};
return std::make_unique<enablePrivateCtor>(std::forward<T>(t)...);
}
void doIt() const {
std::cout << _i << std::endl;
}
};
int main() {
auto a = A::create(7); // <- change 2, sending 7
a->doIt();
}
Compilation Error:
unique_ptr.h: error: calling a private constructor of class 'enablePrivateCtor'
Why the 1st one - with the empty ctor - is OK, while the 2nd - the non-empty ctor - is not?
The default constructor is never inherited. Therefore, the first enablePrivateCtor generates a default constructor, which calls the base class default constructor.
When you inherit a constructor (as in the second case), the new constructor has the same access level as the inherited one. So since A::A(int) is private, so too will be enablePrivateCtor::enablePrivateCtor(int). So you won't be able to construct with it.
If you need to have a private constructor be able to be called indirectly (through make_unique/emplace/etc), then you need to use a private key type. Like this:
class A;
class A_key
{
A_key() = default;
A_key(int) {} //Prevent `A_key` from being an aggregate.
friend class A;
};
class A {
int _i;
public:
A(int i, A_key): _i(i) {}
// no change here!
template<typename... T>
static std::unique_ptr<A> create(T&&... t)
{
return std::make_unique<A>(std::forward<T>(t)..., A_key{});
}
void doIt() const {
std::cout << _i << std::endl;
}
};
...
auto ptr = A::create(7);
A a(7, A_key{}); //Does not compile, since you're not a friend.
A_key is publicly copyable, but it is not publicly default constructible. So non-private code can pass them around, but non-private code cannot create them.
The difference is that enablePrivateCtor automatically gets a default constructor (which is allowed to call A::A).
It doesn't automatically get an integer conversion constructor: add
enablePrivateCtor(int i) : A(i) {}
and see it work.
The code you posted has undefined behavior.
In particular, you allocate an enablePrivateCtor then delete an A.
A better way than this is to use a key type.
class A {
int _i;
A(): _i(7) {}
class construction_token_t {
explicit construction_token_t(int) {}
friend class A;
};
static auto ctor_token() {
return construction_token_t(0);
}
public:
template<class...Args>
A( construction_token_t, Args&&...args ):A(std::forward<Args>(args)...){}
template<typename... T>
static std::unique_ptr<A> create(T&&... t) {
return std::make_unique<A>(ctor_token(), std::forward<T>(t)...);
}
void doIt() const {
std::cout << _i << std::endl;
}
};
We create a token which can grant another class the right to access our private ctor. The only one who can create this token is our class.
We then pass it to make_unique.
An alternative is to use the factory lambda pattern.
template<class F>
struct factory_lambda_t {
F f;
template<class T>
operator T() const { return f(); }
};
template<class F>
factory_lambda_t<std::decay_t<F>>
factory( F&& f ) { return {std::forward<F>(f)}; }
In C++14 this requires move/copy ctors to be public, but in C++17 it doesn't.
class A {
int _i;
A(): _i(7) {}
public:
template<typename... T>
static std::unique_ptr<A> create(T&&... t) {
return std::make_unique<A>(factory([&]{
return A(std::forward<T>(t)...);
}));
}
void doIt() const {
std::cout << _i << std::endl;
}
};
which I think is pretty slick.
This may result in eliding constructors completely in some cases. In others, a single move occurs.

C++ create static object without constructing it (without allocation)

Im writing embedded code that cannot use memory allocation!
Also, static objects (therefore constructed before the microcontroller has executed the main funtion with all its initialization) shall be constructed within the main after the initialization, not before.
The only solution people suggest is to use static object pointers and allocate (construct them with new) during initialization. since this is no option for me, is there no other solution?
what i wanna do is as follows:
class A
{
public:
A(int a, bool b)
: myVal1(a), myVal2(b)
{
}
private:
int myVal1;
bool myVal2;
}
class B
{
public:
B(char x)
: myChar1(x) // <-- NO CONSTRUCTION, NO PARAMETER OF MYOBJECTA
{
}
void init()
{
// now i wanna construct myObjectA
myObjectA(123, false);
}
private:
char myChar1;
A myObjectA; // <-- NO CONSTRUCTION, NO PARAMETER
}
static B myObjectB('F'); // <-- NO CONSTRUCTION, NO PARAMETER OF MYOBJECTA
void global_init()
{
// ... do before construction
// now i wanna construct myObjectA
myObjectB.init();
//... do after construction
}
You can use a storage area large enough to create there an instance of A and placement new to control the time you create it.
As an example:
#include
#include
struct A {
A(int a, bool b): myVal1(a), myVal2(b) {}
void foo() {}
private:
int myVal1;
bool myVal2;
};
struct B {
B(char x): myChar1(x) {}
~B() { if(ptr) { ptr->~A(); } }
void init() {
ptr = new (&storage) A{123, false};
}
A * myObjectA() {
return ptr;
}
private:
char myChar1;
std::aligned_storage_t<sizeof(A), alignof(A)> storage;
A *ptr{nullptr};
};
static B myObjectB('F');
void global_init() {
// ... do before construction
// now i wanna construct myObjectA
myObjectB.init();
//... do after construction
}
int main() {
global_init();
myObjectB.myObjectA()->foo();
}
This won't allocate memory (if I got right what you mean for that) and the instance of A is actually created within B::init (that seems to be a requirement from what you wrote in your question).
If you have access to a compiler that supports C++17 or you can use the C++ Boost Libraries, std::optional is a valid alternative as suggested in another answer. Anyway you didn't specify the revision of the standard to which to adhere, so... Here is a way to go in any case.
Do you have boost available?
#include <boost/optional.hpp>
class A
{
public:
A(int a, bool b)
: myVal1(a), myVal2(b)
{
}
private:
int myVal1;
bool myVal2;
};
class B
{
public:
B(char x)
: myChar1(x) // <-- NO CONSTRUCTION, NO PARAMETER OF MYOBJECTA
{
}
A& getA() {
assert(myObjectA);
return myObjectA.get();
}
void init()
{
// now i wanna construct myObjectA
myObjectA.emplace(123, false);
}
private:
char myChar1;
boost::optional<A> myObjectA; // <-- NO CONSTRUCTION, NO PARAMETER
};
static B myObjectB('F'); // <-- NO CONSTRUCTION, NO PARAMETER OF MYOBJECTA
void global_init()
{
// ... do before construction
// now i wanna construct myObjectA
myObjectB.init();
//... do after construction
}
int main()
{
global_init();
}

C++ template <Base class and Derived class>

I'm an C++ beginner, please help me.
I can't use template class as the constructor' s parameter.
xcode shows 'No matching constructor for initialization of 'Work'' error.
The whole source code below, any one can fix this?
#include <iostream>
class Base {
public:
virtual void hello_world() const {
printf("Base::hello_world()\n");
};
};
class Derived : public Base {
public:
void hello_world() const {
printf("Derived::hello_world()\n");
};
};
template<class T>
class Templ {
public:
Templ(const T &t) : _value(t) {}
const T& getValue() const{
return _value;
}
private:
const T &_value;
};
class Work {
public:
Work(const Templ<Base*> &base) : mBase(base) {}
void working() {
mBase.getValue()->hello_world();
}
private:
const Templ<Base*> &mBase;
};
int main(int argc, const char * argv[]) {
Templ<Base*> base(new Base());
//OK
Work w_base(base);
Templ<Derived*> derived(new Derived());
//error: No matching constructor for initialization of 'Work'
Work w_derived(derived);
return 0;
}
Work w_derived(derived); is never going to work as Work expects a Templ<Base*>. A Templ<Base*> and a Templ<Derived*> are two different, distinct types. Just a like a std::vector<int> is not the same as a std::vector<std::complex>.
What you can do though is create a Templ<Base*> from a pointer to a Dervied and then create a Work with that. Something like
Templ<Base*> derived(new Derived());
Work w_derived(derived);
Live Example
Also as pointed out in the comments since you are using polymorphism you need to have a virtual destructor in the base class. If the destructor is not virtual then only the base class destructor will run and you will your object will not be properly destructed.
In C++, this would look like this
struct Base
{
virtual ~Base() {} // enable descruction of base through pointer to Base
virtual void hello_world() const
{ std::cout<<"Base::hello_world()\n"; }
};
struct Derived : Base
{
void hello_world() const override
{ std::cout<<"Derived::hello_world()\n"; }
};
struct work
{
work(const Base*p)
: ptr(p) {}
void working() const
{ ptr->hello_world(); }
private:
std::unique_ptr<const Base> ptr;
};
int main()
{
Work w_base(new Base);
Work w_derived(new Derived);
w_base.working();
w_derived.working();
}
Note the following
the virtual destructor of Base ensures that a derived class is properly destructed from a pointer to Base, so that std::unique_ptr<> works correctly.
the override keyword ensures that we actually implement a virtual method.
the usage of std::unique_ptr<> avoids the Templ class. Morever, its destructor will automatically and correctly destroy the pointed-to object, avoiding the memory leak of your code.
return 0 is not required for int main(), but automatically generated.

How should I handle setter methods that shoud only be used by the factory class?

I am using a factory class to produce a number of little classes from memory pools. These little classes are constant once they are returned by the factory.
Currently a typical declaration of one of these little objects goes something like this:
class LittleObject
{
public:
...//non-getter and setter member functions
int getMemberVariable1() const;//should be accessible to everyone
void setMemberVariable1(int newMemberVariable1Value);//should only be accessible to factory class
...//more getters and setters
private:
...
};
So, as you can see the getters and setters are both in the public area. But the only time the values should be set is during the time it is being built by the factory class. Now, I can clearly see one option where I move the setter functions to private access and make the factory a friend of the LittleObject class. I find this option a bit inelegant because it exposes other private member functions to the factory. Private member functions which the factory has no business accessing.
So my question is this: What is the best method making it so that only the factory class can use the setter functions?
I would use a friend class:
class LittleObject
{
friend class LittleObjectFactory;
public:
int getMemberVariable();
private:
void setMemberVariable( int value );
};
I would really prefer to friend the factory, but if you need stronger
encapsulation, at the expense of elegance, mabe it can be done
struct LittleData;
class Factory
{
public:
void MakeLittle(LittleData&);
};
struct LittleData
{
int data1;
float data2;
};
class LittleObject
{
public:
LittleObject(const LittleObject&) = default;
LittleObject& operator=(const LittleObject&) = default;
int GetData1() const { return data.data1; }
float GetData2() const { return data.data2; }
static LittleObject MakeOne( Factory& f )
{
LittleObject obj;
f.MakeLittle(obj.data);
return obj;
}
private:
LittleObject();
LittleData data;
};
Looking at what I just wrote... I really prefer friend
Another possibility is stencils.
By that I mean static instances of each LittleObject preset to the required configuration so that the factory simply needs to make a copy.
The copy can be made via the copy constructor or, if you don't want to make one of those (and the objects are trivial) then you could use memcpy().
Here is an example using copy constructors:
class LittleObject1
{
int a;
int b;
public:
LittleObject1(const LittleObject1& o): a(o.a), b(o.b) {}
LittleObject1(int a = 0, int b = 0): a(a), b(b) {}
static LittleObject1 stencil;
int get_a() const { return a; }
int get_b() const { return b; }
};
LittleObject1 LittleObject1::stencil(3, 7); // preset values
class LittleObject2
{
std::string s;
public:
LittleObject2(const LittleObject2& o): s(o.s) {}
LittleObject2(const std::string& s = ""): s(s) {}
static LittleObject2 stencil;
std::string get_s() const { return s; }
};
LittleObject2 LittleObject2::stencil("hello"); // preset values
class Factory
{
public:
template<typename Type>
Type* create() const
{
return new Type(Type::stencil); // make a copy of the preset here
}
};
int main()
{
Factory fact;
LittleObject1* o1 = fact.create<LittleObject1>();
std::cout << o1->get_a() << '\n';
std::cout << o1->get_b() << '\n';
LittleObject2* o2 = fact.create<LittleObject2>();
std::cout << o2->get_s() << '\n';
}
This would only be useful if the values are preset and don't need calculating at run-time.
Rely on const-correctness
You say the objects are constant when they are returned by the factory.
In that case why not just return const objects:
class Factory
{
public:
std::unique_ptr<const DynamicLittleObject> createDynamicLittleObject();
const AutomaticLittleObject createAutomaticLittleObject();
};
Then just ensuring to write their functionality in a const-correct way will give the correct access control.
Some might worry about the case the user might cast away the constness, but there's only so much that is worth doing to protect them from themselves.
You could make the factory a static member function of each object. So each object type knows how to create itself. Then you can have some kind of template function to make creating them a little less typing.
Something a bit like this:
class LittleObject1
{
int a = 0;
int b = 0;
public:
virtual ~LittleObject1() {}
static LittleObject1* create()
{
LittleObject1* o = new LittleObject1;
o->a = 1;
o->b = 2;
return o;
}
};
class LittleObject2
{
std::string s;
public:
virtual ~LittleObject2() {}
static LittleObject2* create()
{
LittleObject2* o = new LittleObject2;
o->s = "hello";
return o;
}
};
template<typename Type>
Type* createType(Type*)
{
return Type::create();
}
int main()
{
LittleObject1* o1 = createType(o1);
LittleObject2* o2 = createType(o2);
}