I need to use a codebase which includes some non-copyable classes. They have declared their assignment operator and copy constructor as private. How can I initialize members of these types in my classes? For example:
class non_copyable; // defined somewhere.
// constructor receives a parameter of type `normal_class'
// my_class.hpp
class my_class {
public:
my_class();
virtual ~my_class();
private:
normal_class good_one;
non_copyable trouble;
};
my_class::my_class() :
good_one(normal_class(0,0)),
trouble(non_copyable(good_one)) { // ====> error
}
Currently I'm using pointer to non_copyable in my_class.
You are calling the copy constructor here, since you are constructing trouble from a temporary non_copyable instance:
trouble(non_copyable(good_one))
// ^^^ temporary non_copyable.
Try this:
trouble(good_one)
You make the trouble for yourself
my_class::my_class() :
good_one(normal_class(0,0)),
trouble(non_copyable(good_one))
should be just
my_class::my_class() :
good_one(0,0),
trouble(good_one)
Assuming trouble class needs good_one to construct; i.e has a constructor that takes a normal_class as parameter; you can try this:
my_class::my_class() :
good_one(normal_class(0,0)),
trouble(good_one) {
}
But in any situation you are relying on initializer list being called in the right order (because trouble depends on good_one being initalized). That might not be the case on a different compiler or even with different compiler options.
I'd suggest not using this pattern, initialize the good_one in the initalizer list and then do something like trouble.SetObject(good_one) in the constructor.
Or maybe convert trouble to
non_copyable* trouble;
[...]
my_class::my_class() :
good_one(normal_class(0,0))
{
trouble = new non_copyable(good_one);
}
Related
I wrote a class that holds an array of objects, which each expect a pointer to a c-struct in their constructor:
Here's some code:
class TheOtherClass
{
private:
SomeCStruct* m_pCStruct;
int m_ObjIdx;
public:
TheOtherClass::TheOtherClass(SomeCStruct* pCStruct, int ClassIdx)
: m_pCStruct(pCStruct),
m_ObjIdx(ClassIdx)
{}
}
class MyClass
{
private:
TheOtherClass m_objects[4];
SomeCStruct* m_pMyCStruct;
public:
MyClass::MyClass(SomeCStruct* pCStruct)
: m_pMyCStruct(pCStruct)
{
m_objects[4] = {TheOtherClass(&pCStruct, 1), TheOtherClass(&pCStruct, 2),
TheOtherClass(&pCStruct,3), TheOtherClass(&pCStruct, 4)};
}
I'd actually like to initialize the member objects of MyClass in the init list of the constructor but I was reading this isn't possible?
Hence I tried the above approach but I'm always getting the error: "TheOtherClass": no appropriate default constructor available.
Another thing to mention is that the compiler I'm running is a state of the art C++03 compiler ;-) (it's code for a DSP and TI sort of doesn't wants to update the compiler)
Any help would be highly appreciated!
Since MyClass(SomeCStruct* pCStruct) constructor doesn't (can't) initialize the TheOtherClass m_objects in the member initializer list, the m_objects will need to be default constructed at first, and then this member will be reassigned with new value in the body of the MyClass constructor.
The TheOtherClass won't have a synthesized constructor since you have defined (other, non-default) ctor that takes SomeCStruct* and int. Thus, no default constructor for it in your code.
Here, we define a default constructor:
class TheOtherClass {
SomeCStruct* m_pCStruct;
int m_ObjIdx;
public:
TheOtherClass() : m_pCStruct(nullptr), m_ObjIdx(0) {} // Default ctor
TheOtherClass(SomeCStruct* pCStruct, int ClassIdx)
: m_pCStruct(pCStruct),
m_ObjIdx(ClassIdx) {}
};
As to the MyClass. Since you've already pass the argument as a pointer MyClass(SomeCStruct* pCStruct), you don't need to take the address of it (as in &pCStruct).
And the main issue in the MyClass is that the m_objects is of class type TheOtherClass which does not define any operator=, including the one that takes brace-enclosed initializer list. So, you won't be able to do like so:
m_objects[4] = { // The operator '=' has not been defined for the TheOtherClass, also note the subscript
TheOtherClass(pCStruct, 1),
TheOtherClass(pCStruct, 2),
TheOtherClass(pCStruct,3),
TheOtherClass(pCStruct, 4)
};
Again, when you try to list initialize it, it has already been default constructed. Thus, for this approach to work you will need to define such copy-assignment operator to do this kind of assignment to an already default constructed instance of the TheOtherClass. As to how to implement this operator, it will mostly depend on the overall design and other factors which were not mentioned in this thread.
Update
If you will have the following body of the MyClass constructor, it will work:
MyClass(SomeCStruct* pCStruct) : m_pMyCStruct(pCStruct) {
// Assign to default initialized TheOtherClass object
m_objects[0] = TheOtherClass(pCStruct, 1);
}
I would advise you to check these:
Operator overloading
Constructors and member initializer lists
I have a class which looks something like this:
class X
{
std::variant<CComPtr<ID2D1HwndRenderTarget>, CComPtr<ID2D1DCRenderTarget>> mpRenderTarget;
void init();
}
// To early to know which member we need, it's no value until derived class calls init() method.
X::X() : mpRenderTarget(nullptr)
void X::init()
{
// Only here it is known what exactly to initialize!
// and this is going to be in derived class, this one pure virtual.
}
Problem is that this constructor doesn't compile, because variant initialization is not valid,
however if I omit initialization of mpRenderTarget in constructor then another problem is that
the variant is "ilformed" according to standard.
I just want to initalize it to some default in constructor, but how?
Simply assign it a default value of one of the types:
X::X() : mpRenderTarget(CComPtr<ID2D1HwndRenderTarget>{}) {}
You can be explicit about the default pointer value being nullptr as well:
X::X() : mpRenderTarget(CComPtr<ID2D1HwndRenderTarget>{nullptr}) {}
The important part is that you must specify which of the variant types you want to initialize the object with.
Observation: The constructor of ClassMain needs to call Init before it can constructor a member variable a. Since the ClassA has no default constructor, the code doesn't compile.
ClassA
{
public:
// This class has no default constructor
ClassA(...){}
};
class ClassMain
{
public:
ClassMain(...) {
Init(...);
a = ClassA(...); // error: ClassA has no default constructor
// a has to been constructed after the Init is called!
}
ClassMain(...) {
Init(...);
call other functions
a = ClassA(...);
}
private:
// initialize environment
void Init(...) {}
private:
ClassA a;
};
Question> The simple solution is to provide a default constructor for ClassA. However, I would like to know whether there is a better solution to address the issue above?
The better solution is not to require an Init function at all. You're trying to reinvent constructors, and breaking their design in the process.
If Init does too much work for a constructor, then do it outside and pass the resulting resources into ClassMain as a constructor argument; notice how you're already doing all the work in the constructor's scope anyway, thereby not gaining anything appreciable over proper initialisation.
Of course, if you must perform a ton of work before initialising a, and you cannot pass in a ClassA& from the outside and initialise from that, then you're simply going to have to have a be an indirect member.
There is one nasty workaround you could use: have Init actually be a base constructor...
The obvious solution is to call Init() from the initializer list of an early member or a base class. Once this subobject is constructed its results can be passed to the constructors of other subobjects. For example, when defining stream classes I typically privately inherit from a virtual base containing the stream buffer:
struct somebuf_base {
somebuf sbuf;
// ...
};
class somestream
: private virtual somebuf_base
, public std::ostream
{
public:
somestream(someargs)
: somebuf_base(someargs)
, std::ostream(&this->sbuf) {
}
// ...
};
Since base classes are constructed in the order they appear but virtual bases before non-virtual bases, the base class containing the sbuf member is constructed first. Its constructor replaces your Init() function.
When using C++ as of the 2011 revision, you might also use forwarding constructors to share logic between multiple constructors.
It's easier to take a pointer to ClassA; So, you can instantiate it whenever you want.(after the init())
If you used a pointer, don't forget to implement the virtual destructor and release the allocated memory for the ClassA *a
If you absolutely must call some function at the start of your constructor, and can't put that setup into some base class or early-constructed member, you could use this ugly trick:
ClassMain::ClassMain(int main_param)
: a( (Init(init_arg), class_a_arg1), class_a_arg2 )
{
}
In this case: No, we cannot avoid that.
The reason is that when calling Init or any other member function you are guaranteed by the language that the object you are in exists. As a is a member of ClassMain it must be constructed before any function in ClassMain can be called.
The only chance that you have here is to refactor the code.
Sometimes I don't want to provide a default constructor, nor do I want the compiler to provide a system default constructor for my class. In C++ 11 I can do thing like:
class MyClass
{
public:
MyClass() = delete;
};
But currently my lecturer doesn't allow me to do that in my assignment. The question is: prior to C++ 11, is there any way to tell the compiler to stop implicitly provide a default constructor?
I would say make it private.. something like
class MyClass
{
private:
MyClass();
}
and no one(from outside the class itself or friend classes) will be able to call the default constructor. Also, then you'll have three options for using the class: either to provide a parameterized constructor or use it as a utility class (one with static functions only) or to create a factory for this type in a friend class.
Sure. Define your own constructor, default or otherwise.
You can also declare it as private so that it's impossible to call. This would, unfortunately, render your class completely unusable unless you provide a static function to call it.
Since c++11, you can set constructor = delete. This is useful in conjunction with c++11's brace initialization syntax {}.
For example:
struct foo {
int a;
foo() = delete;
foo(int _a) {
// working constructor w/ argument
}
};
foo f{}; // error use of deleted function foo::foo()
foo f{3}; // OK
see https://en.cppreference.com/w/cpp/language/default_constructor#Deleted_implicitly-declared_default_constructor
Additionally to declaring the default constructor private, you could also throw an exception when somebody tries to call it.
class MyClass
{
private:
MyClass()
{
throw [some exception];
};
}
I'm not an C++ expert and still do not have a great intuitive grasp of how things works. I think this is a simple question. I am having trouble passing objects with state to other objects. I'd prefer to avoid passing pointers or references, since once the initialized objects are setup, I call them millions of times in a tight loop. I think I'm dong something like a Command pattern. Here's the core of the problem. My header code is something like:
class ObjectWithState {
public:
ObjectWithState(int state) { // This constructor creates the problem!
state_ = state; // everyting works with no constructor.
}
private:
int state_;
};
class TakesObject {
public:
TakesObject(ObjectWithState obj) {
obj_ = obj;
}
private:
ObjectWithState obj_;
};
My main() functions looks like:
int main () {
ObjectWithState some_object(1);
TakesObject takes_object(some_object);
return 0
}
I get the following error (g++):
test.h: In constructor 'TakesObject::TakesObject(ObjectWithState)':
test.h:14: error: no matching function for call to 'ObjectWithState::ObjectWithState()'
test.h:5: note: candidates are: ObjectWithState::ObjectWithState(int)
test.h:3: note: ObjectWithState::ObjectWithState(const ObjectWithState&)
Simple answer?
I not sure if this has to do with copy constructors. If so, I'm trying to find a solution that keeps the class definition of ObjectWithState very clean and short. Users of this library will be defining lots of small functions like that which will be used by TakesObject function. Ideally programmers of the ObjectsWithState just need to focus on implementing a simple object. Perhaps I'm going astray...
What you may want to do is use the member initialisation syntax:
class TakesObject {
public:
TakesObject(ObjectWithState obj): obj_(obj) {
}
private:
ObjectWithState obj_;
};
In your posted code, the TakesObject constructor will first try to construct a new ObjectWithState with its default constructor, then call the assignment operator to copy the passed-in obj to obj_. The above example constructs the obj_ directly using its copy constructor.
You will also need to define a copy constructor for your ObjectWithState class, too:
class ObjectWithState {
public:
ObjectWithState(int state) {
state_ = state;
}
ObjectWithState(const ObjectWithState &rhs) {
state_ = rhs.state_;
}
private:
int state_;
};
If you omit all constructors from your class declaration, then the compiler supplies a default and a copy constructor for you. If you declare any constructors, then the compiler supplies no default or copy constructor, so you must implement your own.
You're getting this error because you're declaring a constructor. The compiler will provide the default constructor only if you don't declare a constructor in your class. Because you have declared a constructor, you don't get the default one. You have to explicitly declare a constructor with no parameters.