I have a class MyClass in which I need to create a std::array of std::vector in the default constructor. However, this class has a data member which is a reference (of type Something) which also needs to be initialized in the constructor and I cannot do this in a default constructor.
How should I solve this?
class MyClass{
public:
MyClass(); //Cannot instantiate s??
MyClass(Something& s);
Something& s;
}
MyClass array[10]; // MyClass needs a default constructor but a default
// constructor won't be able to initialize s
A class with a reference member needs to set the reference in its constructors. In most cases this means, that the class cannot have a default constructor. The best way to solve the problem is use a pointer instead of a reference:
class MyClass{
public:
MyClass() : s_(0) {}
MyClass(Something* s) : s_(s) {}
Something* s_;
}
As I commented above, by the description alone, I would say that it's a classical case where s should be a Something* rather than a Something&...
OTOH, this work perfectly, so you don't need a default constructor if you just initialize each element of your array:
struct Something { };
struct MyClass {
MyClass(Something& ss) : s{ss} {}
Something& s;
};
int main() {
Something a, b, c, d;
Something v[10] = { a, b, c, d, a, b, c, d, a, b };
return 0;
}
Your can also do this:
class MyClass{
public:
MyClass() : s_(0) {}
MyClass(Something& s) : s_(&s) {}
Something* s_;
}
You can use std::optional<std::reference_wrapper<Something>> instead of Something&, this way you simulate Something* behavior but using modern C++ concepts.
note that you should check if member variable defined this way has value by calling has_value function and get the actual reference value by s_.value().get()
you also could use std::optional<std::reference_wrapper<const Something>> if you need a const reference to Something
Related
I am honestly not sure how to google this even, and as my attempts have failed to do so, can you tell me how to write a prototype of constructor so that I can use it this way?
// MyClass.h
class Object;
class MyClass {
Object a;
Object b;
std::string c;
public:
MyClass(int, int, std::string&); // I do not know how to declare this properly
};
// so that I can write this:
MyClass::MyClass(int a, int b, std::string& c = "uninstantialised") : a(a), b(b) {
this->c = c;
}
// so that when I call the constructor like this:
Object a();
Object b();
MyClass mc(a, b);
// it doesn't produce an error when std::string argument is not specified.
Thanks!
Default arguments need to be specified in the declaration, not in the implementation. Furthermore, you should take the string by value, not by reference, and move it into the MyClass::c member:
public:
MyClass(int a, int b, std::string c = "uninstantialised");
// ...
MyClass::MyClass(int a, int b, std::string c)
: a(a), b(b), c(std::move(c))
{ }
Taking by value and using std::move() is not required, but recommended as it can be more efficient since it avoids copying the string in some cases.
I recommend renaming private data members to something that avoids the same name being used for something else. Here, c is both the private member as well as the constructor parameter. You should use something different for the members. Like a_, b_ and c_ for example. Appending an underscore is a popular way to name private data members.
I have this problem with getting class to pass as a parameter for another class constructor. Basically the code is like this:
class A
{
public:
int ID;
A(int getID)
{
ID = getID;
}
}
and I want to use that class A as a member of class B like this:
class B
{
public:
A someA;
A someB;
int number;
B(A ObjectA, A ObjectB, int getNumber)
{
someA = ObjectA;
someB = ObjectB;
number = getNumber;
}
};
The errors are basically saying that there is no matching function to call B::B(). I don't know whats wrong with it. I have done similar things with vectors of object, so I thought why cant this thing works. Any inputs/correction is appreciated, thank you!
Sidenotes: I have tried adding a default constructor for B as suggested in another thread, but it ended up saying invalid use of B::B.
Use the initialization list for your objects:
B(A ObjectA, A ObjectB, int getNumber)
:someA(std::move(ObjectA)), someB(std::move(ObjectB)), number(getNumber)
{
}
This will use the default move constructor from your class.
I used move here because you are passing your objects by value, so it makes sense to move them. If you passed them by const&, then don't move them and use the default copy constructor.
Still, this is about A default constructor, there is no problem with the B default constructor in the code you showed.
The error happens because you didn't tell B how to initialize its A attributes, so it's looking for the default A constructor.
In C++ the attribute initialization is made as show by Matthieu, and not in the constructor's body. You can add a default A constructor, but you have to think if it's the behavior you want for your code.
Compiling the code below doesn't show any error.
class A {
public:
int ID;
A() {}
A(int getID) {
ID = getID;
}
};
class B {
public:
A someA;
A someB;
int number;
B() {}
B(A ObjectA, A ObjectB, int getNumber) {
someA = ObjectA;
someB = ObjectB;
number = getNumber;
}
};
I have three classes; their function definitions are in a sperate file.
I'm trying to construct an object with various parameters inside another class without using inline implementation.
class A{
public:
A(){}
};
class B{
public:
//takes in two ints, one reference to object, and a string
B(int x, int y, A &a, std::string s );
};
class C{
public:
//in the constructor, construct b_obj with its parameters
C();
private:
B b_obj;
};
How can I make the C constructor construct b_obj with its parameters of the int, the reference to an instance of A, and the string? I tried some methods but I get an error that complains about no match call to the b_obj constructor.
Use an initializer:
C() : b_obj(5, 6, A(), ""){}
This line technically won't work, though, because B's constructor takes an A&, so you can't bind a temporary to it. const A & if it's not being changed, or A if it is, would work out better if you don't have a non-temporary A to pass in.
You need to pass the relevant items to a constructor of object C, and then use an initializer.
class C {
public:
C(int x, int y, A& a, std::string s) : b_obj(x, y, a, s) {}
Say I have a class with some const reference member variable and I would like to forbid a certain type of construction. So I would declare the according constructor private. Of course, a constructor must initialise all const reference member variables of the class. Doing so, however, results in odd looking code:
class A {
};
class B {
B(const A& a): host(a) {}
private:
B():host(A()) {} // This is ugly and not needed !!
const A& host;
};
Is there another way to prohibit a certain construction type except than declaring the constructor private? I do not want to let the compiler write a constructor for me.
Simply don't define this:
B():host(A()) {} // This is ugly and not needed !!
That is, the following should do what you want to do:
class B {
B(const A& a): host(a) {}
private:
//B():host(A()) {} // This is ugly and not needed !!
const A& host;
};
The idea is if you've defined a constructor that takes parameter(s), then the default constructor is not generated by the compiler. That means, instances of the above class cannot be default created!
B b1; //error - needs default constructor which doesn't exist!
B b2(a); //ok - only way to create an instance!
C++11 solution
In C++11, you can explicity tell the compiler not to generate a particular constructor as:
struct B
{
B(const A &a) {}
B() = delete; //disable
};
Not only that. There is more to it, as explained below:
Now the interesting part
You can also selectively disable constructor(s) for selected types which makes delete more interesting. Consider this,
struct A
{
A (int) {}
};
Object of this class can be created not only with int argument, but any type which implicitly converts to int. For example,
A a1(10); //ok
A a2('x'); //ok - char can convert to int implicitly
B b;
A a3(b); //ok - assume b provides user-defined conversion to int
Now suppose, for whatever reason, I don't want the users of class A to create objects with char or class B , which fortunately or unfortunately can implicitly convert to int, then you can disable them as:
struct A
{
A(int) {}
A(char) = delete; //disable
A(const B&) = delete; //disable
};
Now here you go:
A a1(10); //ok
A a2('x'); //error
B b;
A a3(b); //error - assume (even if) b provides user-defined conversion to int
Online Demo : http://ideone.com/EQl5R
The error messages are very clear:
prog.cpp:9:5: error: deleted function 'A::A(char)'
prog.cpp:10:5: error: deleted function 'A::A(const B&)'
Just leave it out. As soon as you provide a custom constructor, no other constructor is auto-generated (except for a copy constructor).
If you want to forbid any construction – ending up with a class that has only static members – you can simply declare the constructor as private, and not define it. Such a class is very rarely useful in C++ (since you cannot create instances of it); the only purpose that I can think of is to implement trait classes:
template <typename T>
struct type_to_color {
static char const* value() { return "blue"; }
private:
type_to_color();
};
template <>
struct type_to_color<int> {
// Integers are red!
static char const* value() { return "red"; }
private:
type_to_color();
}
char const* char_color = type_to_color<char>::value();
char const* int_color = type_to_color<int>::value();
However, this is extremely uncommon: trait classes are abundant in C++ but they never declare their constructors as private, it’s just assumed that everybody knows not to instantiate them.
I'll post the C++11 solution: delete the constructor.
class B {
B() = delete;
B(const A& a): host(a) {}
private:
const A& host;
};
As Konrad Rudolph sayd: as soon you provide a custom constructor, no other constructor is auto-generated (except for a copy constructor).
Therefore, other options are:
Declare the constructor private (so that you can't inherit from your class), but do not provide a definition:
class B {
public:
B(const A& a): host(a) {}
private:
B(); // not implemented!
const A& host;
};
Or in C++11, as R. Martinho Fernandes says:
class B {
public:
B() = delete;
B(const A& a): host(a) {}
private:
const A& host;
};
Let's say that I have two classes A and B.
class A
{
private:
int value;
public:
A(int v)
{
value = v;
}
};
class B
{
private:
A value;
public:
B()
{
// Here's my problem
}
}
I guess it's something basic but I don't know how to call A's constructor.
Also the compiler demands a default constructor for class A. But if A has a default constructor than wouldn't the default constructor be called whenever I declare a variable of type A. Can I still call a constructor after the default constructor has been called? Or can I declare an instance of a class and then call a constructor later?
I think this could be solved using pointers but can that be avoided ?
I know that you can do something like this in C#.
EDIT: I want to do some computations in the constructor and than initialize the classes. I don't know the values beforehand.
Let's say I'm reading the value from a file and then I initialize A accordingly.
The term you are looking for is initializer list. It is separated from the constructor body with a colon and determines how the members are initialized. Always prefer initialization to assignment when possible.
class A
{
int value;
public:
A(int value) : value(value) {}
};
class B
{
A a;
public:
B(int value) : a(value) {}
}
I want to do some computations in the constructor and than initialize the classes. I don't know the values beforehand.
Simply perform the computation in a separate function which you then call in the initializer list.
class B
{
A a;
static int some_computation()
{
return 42;
}
public:
B() : a(some_computation()) {}
}
You use an initialization list to initialize the member variables.
public:
B() : value(4) { // calls A::A(4) for value.
}
Or can I declare an instance of a class and then call a constructor later?
Yes, you can do that. Instead of (A value;) declare (A* value;), and then B's constructor will be B():value(new A(5)){}.
In the B's destructor you will have to do delete value;
I think this could be solved using pointers but can that be avoided ?
Yes. Use shared_ptr.
Try:
B() :
value(0)
{
// Here's my problem
}