Pretty basic question. Has to do with design preference but I think there are some caveats that I'm missing.
I want to have an instance of some class inside another class. The way I see it, I have 3 options:
Have the object class2 inside class1 as I have it below. This means it gets constructed when an instance of class1 gets constructed. My issue here is that it may need a default and/or copy constructor? When I instantiate a class1 object below, does it construct class2_inst(args) using that constructor? Or is class2_inst already created with some default constructor and the line class2_inst(args) simply copies an anonymous class2 object?
class class2 {
public class2(args) {
...
}
}
class class1 {
protected class2 class2_inst;
public class1(args) : class2_inst(args) {
...
}
}
I can have a pointer to class2:
class class2 {
public class2(args) {
...
}
}
class class1 {
protected class2* class2_inst;
public class1(args) {
class2_inst = new class2(args);
...
}
}
This has the advantage that class2 isn't instantiated until I explicitly call the new operator.
Then there's using a reference instead of a pointer:
class class2 {
public class2(args) {
...
}
}
class class1 {
protected class2& class2_inst;
public class1(args) {
class2_inst = new class2(args);
...
}
}
I want class2_inst's life-time to match that of class1. No funny business with having class2_inst living outside of class1. So references may be the way to go.
Of the 3 methods, which is everyone's preferred way, and why?
Your examples are a bit backwards. I think you meant this instead:
class class2 {
public:
class2(args) {
...
}
};
class class1 {
protected:
class2 class2_inst;
public:
class1(args) : class2_inst(args) {
...
}
};
class class2 {
public:
class2(args) {
...
}
};
class class1 {
protected:
class2* class2_inst;
public:
class1(args) {
class2_inst = new class2(args);
...
}
};
class class2 {
public:
class2(args) {
...
}
};
class class1 {
protected:
class2& class2_inst;
public:
class1(args) {
class2_inst = new class2(args);
...
}
};
Now, that being said:
Example 1 will work nicely for what you want, and is the preferred way to go: "I want class2_inst's life-time to match that of class1. No funny business with having class2_inst living outside of class1." And to answer your question - yes, class2_inst(args) will construct the class2_inst member directly, not default-construct it and then copy an anonymous class2 object into it. See Constructors and member initializer lists.
Example 2 will also work, but to make it work correctly, you need to add a destructor to class1 to delete the new'ed class2 object, and you need to also add copy/move constructors and copy/move assignment operators to class1 to copy/move class2 objects properly, per the Rule of 3/5/0.
class class2 {
public:
class2(args) {
...
}
};
class class1 {
protected:
class2* class2_inst;
public:
class1(args) {
class2_inst = new class2(args);
...
}
class1(const class1 &src) {
class2_inst = new class2(*(src.class2_inst));
...
}
class1(class1 &&src) {
class2_inst = src.class2_inst;
src.class2_inst = nullptr;
...
}
~class1(args) {
delete class2_inst;
}
class1& operator=(const class1 &rhs) {
if (this != &rhs) {
class1 tmp(rhs);
std::swap(class2_inst, tmp.class2_inst);
}
return *this;
}
class1& operator=(class1 &&rhs) {
class1 tmp(std::move(rhs));
std::swap(class2_inst, tmp.class2_inst);
return *this;
}
};
You get all of that functionality for free in Example 1, as the compiler will auto-generate those extra methods for you. Yes, there are valid cases where it makes more sense to use pointers for members, but this example is not one of them.
Example 3 is simply not valid. You can't assign an object pointer to an object reference. And why would you think "references may be the way to go"? They are not, in this case.
So, in a nutshell, you should prefer value sematics whenever possible, but use pointer/reference semantics when needed.
Related
Suppose I have a class like the following:
class A { virtual ~A(); ... }
class B : public A { ... }
class C : public A { ... }
I also have a vector of unique_ptr which is declared this way:
std::vector<std::unique_ptr<A>> vec;
Assume vec is populated with unique_ptr to objects of derived class. What should I do if I want a deep copy of any of the vector elements, either b or c, and let a base class unique_ptr pointing to it? Originally I was doing things like
std::unique_ptr<A> tmp = std::make_unique<A>(*b);
I don't think this is correct.
One possible solution is to declare a virtual cloning method in the base class and override it for each subclass:
class A {
virtual ~A() {}
virtual std::unique_ptr<A> clone() const = 0;
}
class B : public A {
std::unique_ptr<A> clone() const override {
return std::unique_ptr<A>(new B(*this));
}
};
Edit:
An usage example:
void f(const A& original) {
std::unique_ptr<A> copy = original.clone();
// Here, copy points to an instance of class B.
}
I need to delay the constructor call, so I can initialize the value that should be passed to the constructor. I have written a short and very simplified example.
class A
{
private:
ObjectA* _ptr;
public:
A(ObjectA*);
};
class B
{
private:
A object; // The constructor seems to be called here?
ObjectA* obj;
public:
B();
};
A::A(ObjectA* ptr)
{
this->_ptr = ptr;
}
B::B()
{
obj = new ObjectA();
object(obj); // I want to call the 'A' constructor here, after initializing of 'obj'.
}
Is it possible?
No, you cannot defer a construction of a value member. You can use a pointer instead of a direct value but that's no solution for your problem.
The proper solution for your problem is using initialization list:
B::B ( ) : obj(new ObjectA), object(obj) {}
Also, you have to put obj before object in class B:
class B
{
private:
ObjectA *obj;
A object;
public:
B ( );
}
The reason for this is that, when a constructor is called, all of the objects members must be properly constructed and initialized. This is done using their default constructor.
The reason for reordering the class members is that the initializers of the members are called in the order they are declared in the class not in the order of their appearence in the initialization list.
Here is a solution I would use.
class B
{
private:
char m_aBytes[sizeof(A)];
A& getA () { return *(A*)m_aBytes; }
public:
B ()
{
ObjectA* objA = new ObjectA ();
new (m_aBytes) (objA);
}
}
I have the following situation:
Factory class implementation:
class FactoryClassA {
public:
FactoryClassA(){};
~FactoryClassA(){};
ClassA create(double a, double b) {
return ClassA;
};
};
ClassA implementation:
class ClassA {
friend class FactoryClassA
~FactoryClassA() {}
private:
ClassA(double a = 0, double b = 0)
{
a(a),
b(b)
};
double a;
double b;
};
OtherClass implementation:
class OtherClass {
public:
OtherClass() {
ClassFactory myClassAFactory;
aClassA = myClassAFactory.create();
}
~OtherClass() {};
private:
aClassA;
};
Unfortunately, this does not work. Because in the (empty) member initializer list of OtherClass, the empty constructor of ClassA is called, which does not exist.
The reason why I made the constructor of ClassA private, is that I want the user to only create objects of ClassA through the FactoryClassA.
First of all, create() should have a return type:
ClassA create(double a, double b) {
return {a, b};
}
I am not going through all the other syntactic bugs. Please fix them, because others might find your question when they search for answers. Please make it easy for them to understand your question.
This has to be in your code after ClassA has been defined, because the compiler has to know the size of a ClassA object as well as the constructor to use.
Second, just initialize your members before the constructor body:
OtherClass() :
aClassA{ClassFactory{}.create(0.0, 0.0)}
{}
You might prefer to have create() as a static member function of ClassFactory. Then the OtherClass constructor looks like this:
OtherClass() :
aClassA{ClassFactory::create(0.0, 0.0)}
{}
That is more intuitive.
This code is full of small errors, that make it unuseable. First syntax for member initialization for a class is :
class A {
A(x, y): m_a(x) { ... }
...
}
and class declaration are statements so they need the final ;
Next, the dtor for a class must be public if you want to be able to use temporary objects.
The default values should not be in ClassA ctor but on create factory method.
Finally, you should use a static factory in OtherClass, no need to create a new factory for each object.
Code becomes:
class FactoryClassA;
class ClassA {
public:
friend class FactoryClassA;
~ClassA(){}
private:
ClassA(double a, double b):a(a),b(b){}
double a;
double b;
};
class FactoryClassA {
public:
FactoryClassA(){}
~FactoryClassA(){}
ClassA create(double a = 0, double b = 0) {
return ClassA(a, b);
}
};
class OtherClass {
public:
OtherClass(): aClassA(myClassAFactory.create()){}
~OtherClass(){}
private:
static FactoryClassA myClassAFactory;
ClassA aClassA;
};
FactoryClassA OtherClass::myClassAFactory;
Not far from original one, but this ones compiles...
I have some code like that:
class Class1 {
// some code here
Class2 inverse() {
// some code here
}
};
class Class2 : public Class1 {
// some code here
};
And I got an error that Class2 is unknown in function inverse. Can I declare that class and how?
You can forward declare Class2 before Class1. And then you'll have to separate your inverse function declaration from definition. So you should define its after Class2 is defined:
class Class2;
class Class1 {
// some code here
Class2 inverse();
};
class Class2: public Class1 {
// some code here
};
Class2 Class1::inverse()
{
return Class2();
}
But, honestly, it is bad design.
Class2 is not defined when you reach Class2 inverse() so even forward declaring would not completely solve that issue. However you can do two things. Either return a pointer or a reference to Class2
class Class2;
^^^^^^^^^^^^
class Class1 {
Class2& inverse() {
// ^^^ or possibly Class2*
//
}
};
class Class2: public Class1 {
}
Or defined inverse after Class2 have been completely defined
class Class2;
class Class1 {
Class2 inverse();
};
class Class2: public Class1 {
//
}
Class2 Class1::inverse() {
//
}
I would go with the second option if possible.
For C++ learning purposes, I have the files class1.h, class1.cpp, class2.h and class2.cpp. I would like to instantiate an object named class1Obj inside class2. Where and how do I instantiate this object? Do I instantiate classObj inside the class2 constructor?
In the past I have created a pointer to a class, which worked well for that time, but I think a pointer is not the route I should take this time because the classObj will only be used inside class2.
class class1
{
//...
};
class class2
{
class1 member;
//...
};
In class2 ctor, you can initialize member in the constructor initialization list.
class2::class2(...)
: member(...)
{
//...
}
Well how did you create a pointer in the past? Presumably, you did something like this:
class class2
{
public:
class2()
{
class1Pointer = new class1();
}
// Destructor, copy constructor/assignment, etc...
private:
class1* class1Pointer;
};
Now you want to do exactly the same but this time you don't want a pointer to class1, you want a class1 itself:
class class2
{
public:
class2() {}
// Destructor, copy constructor/assignment, etc...
private:
class1 class1Obj;
};
The object will be default initialized when your class2 object is created. If your class1 constructor should take some arguments, use an initialization list:
class class2
{
public:
class2() : class1Obj(1, 2, 3) {}
// Destructor, copy constructor/assignment, etc...
private:
class1 class1Obj;
};
Instantiate a class inside a class :
#include <iostream>
using namespace std;
class Foo
{
public:
Foo(int i)
{
}
};
class Bar
{
Foo i; //<--- instantiate a class inside a class ----
public:
Bar() : i(1) //<--- instantiate a class inside a class ----
{
}
};
int main(void)
{
Bar b;
cout<<" \nPress any key to continue\n";
cin.ignore();
cin.get();
return 0;
}
It depends on your Class1. If its constructor accepts some parameters, then you must initialize it explicitly in Class2 constructor or in initialization list.
Class2 {
public:
class2() {
//Here m_class1Obj will be instantiated
m_class1Obj = Class1(/*some params*/);
}
private:
Class1 m_class1Obj;
};
Or
Class2 {
public:
class2() : m_class1Obj() {}
private:
Class1 m_class1Obj;
};