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) {}
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.
Consider this example:
class C {};
class B {
public:
B(C& c) : c_(c) {};
private:
C& c_;
};
class A {
public:
A(C& c) : b(c) {};
private:
B& b;
};
A has a reference member b of class B. B has a constructor that takes a reference of class C. A's constructor takes a reference of class C and tries to initialize b by calling the latter's constructor with c.
But clang complains with the following message:
wtf.cpp:12:13: error: non-const lvalue reference to type 'B' cannot bind to a value of unrelated type 'C'
A(C& c) : b(c) {};
^ ~
1 error generated.
It sounds almost as if clang thought I was assigning c to b, but my intent is to call B's constructor with c. What am I doing wrong here?
What you describe is not restricted to initializer lists, but to the usual construction of references. The following should not compile:
class C
{};
class B
{
public:
B(C& c)
: c_(c)
{}
private:
C& c_;
};
int main()
{
C c;
B b0(c); // This works because you actually create an object of type B.
B& b1(c); // Error, cannot construct reference to B from C.
B& b2 = c; // Same as above, only a different notation.
// You cannot write a constructor of B to make these lines work,
// because you do not create an object of type B.
}
An object of class B can be constructed from a reference to a C object, but the same does not hold for references. A reference can only be created from an object of the same type, or a type below in inheritance hierarchy.
That is exactly the point of a reference: You do not construct an object. You just introduce a new name for an object that was created somewhere else.
You cannot do that. A reference is in fact close to non modifiable pointer. That means that a B& can only reference a object of class B or of a sub-class of B. And there is nothing like the construction of a reference.
You could construct a temporary B object from the C reference, but you cannot initialize a reference with a temporary object, because as soon as the reference will have be initialized, the referenced object would be destroyed.
So you must store a real object an not a ref in A:
class A {
public:
A(C& c) : b(c) {};
private:
B b;
};
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
I am spoiling the comma operator in the constructor, so that I can preprocess the parameters and use the processed parameter for initialization.
Say I have the following base and derived classes:
class Base
{
protected:
int i;
int j;
public:
Base(int a):i(a),j(a){}
Base(int a, int b):i(a),j(b){}
};
class Derived:public Base
{
private:
int d;
void inc(int & a) {a++;}
void inc(int & a, int & b) {a++; b++;}
public:
Derived(int a, int b, int c);
};
I know I can use comma operator to process a parameter and use it to initialize the base part as follows:
Derived::Derived(int a, int b, int c):Base((inc(a),a)),d(c){}
Now what about if I want to pre-process two parameters and use them to initialize the base? I did the following:
Derived::Derived(int a, int b, int c):Base((inc(a,b),(a,b))),d(c){}
But this is not what I want, because the single-parameter base constructor will still be used (since (a,b) is also a comma operator that returns b). Is there anyway to achieve what I want if comma operator is not possible?
Although this can constitute as a mere opinion, I would recommend against writing such expressions; you are putting an extra burden on the maintainer to figure out what exactly is being executed before the actual function is called; even figuring out the number of arguments the function takes will require some effort, because commas would normally be used in that context only to separate arguments.
For a single argument, I would do it like this:
class Derived : Base {
static int computeSomething(int a) { return a+1; }
Derived(int a) :
Base(computeSomething(a)), ...
{ ... }
};
Note I'm returning a new value. It could even take and return by reference if the type of a is too expensive to copy.
For multiple arguments, all needed to be updated at once, I would change the base to receive the entire pack of arguments with a named entity, or maybe a std::tuple, and do it like the single-argument version.
what i have is two classes
//class A is a kind of config-class which have configuration details
//for class B
class A{
//....
};
class B{
public:
B(A const& _a)
:a(_a){}
private:
A const& a;
};
till this point everything is fine.
now i want is a B::configure(A const& _a) function so that i can dynamically pass the reference of configuration class A to class B which shall be assigned to the member variable a. but i'm not able to change the member variable B::a as it is a const&.
what can be the work around?
i think #Seth Carnegie's approach is better, i should use a pointer to class A inside class B in this way:
class B{
public:
B(A const& _a)
:a(_a){}
configure(A const& _a)
{ a = &_a; }
private:
A const* a;
};
You cannot alter a reference after it's been initialized, whether it is const or not. The const just keeps you from making alterations to the variable that the reference refers to. So to change the variable after the instance has been constructed, you'll have to use pointers rather than references.
Your grammar was rather unclear, tell me if I misunderstood the question.