If I have 2 classes, one with a custom constructor, and the other with an instance of the first class. How do I create that instance with the custom constructor.
For example:
a.h
class A
{
public:
A(std::string input);
};
b.h
Class B
{
public:
A a("Greetings");
};
This wouldn't work properly, it gives the error "expected a type specifier" on the string itself, and whenever I use a member of class A in class B, it says "expression must have a class type"
I'm assuming this means I'd need to make it
A a(std::string words);
But I'm not sure where or how I would define what the string should be.
Use the constructor's initialization list:
class A
{
public:
A (std::string input);
};
class B
{
A a;
public:
B (std::string s) : a (s) {}; //This calls the constructor of A on 'a'
};
Also, in C++11 you can use the uniform initializer syntax:
class B
{
A a {"Greetings"}.
...
};
But with this, you can only call the constructor with a compile-time constant.
Related
I want to write a C++ program in which an object of a parameterized class A-'a' has to be initialized inside another class B. I should not/can not initialize like 'A a(parameter list);' of class A while declaring the object variable 'a' which is outside the constructor of class B. The necessary parameters to the object 'a' are gotten through the constructor of B. How to initialize 'a' inside B's constructor with the required parameters?
Class A{
public:
A(string s)
{cout<<s;}
};
class B{
private:
A a;
public:
B(string path){
a(path);
}
};
With the above code I am getting errors. How to initialize the object a(path) inside the class B?
The feature you are looking for is member initializer list. In your example it would be used like this:
class B{
B(string path) : a(path) {
}
};
I was happy to find out that in C++11 we can inherit constructors like:
class Foo
{public:
Foo(int a, double b, std::string name, char somethingElse);
};
class Derived : public Foo
{public:
using Foo::Foo;
};
But I'm finding that I'm often extending the base class where there might be maybe one or two extra features, and I need to initialise a couple extra members by maybe passing as extra argument or something. In this case is seems I have to rewrite the constructor and pass all the arguments to the base one. I'm wondering if there is a better solution. I thought maybe just using the inherited constructor and then initialising the extra member on the next line after construction, but it doesn't seem right:
Derived d = Derived(6, 6.0, "name", 'a');
d.extraMember = 4;
d.funcptr = &somefunction;
I figured it was an excellent feature but then I realised more and more that my extended classes needed extra initialisation info.
Here's my example from my code:
struct Window
{
Window(Window* parent, vec2 position, vec2 size, const String& name);
};
struct ConsoleWindow : Window
{
using Window::Window;
// But I've had to rewrite the constructor again because in this constructor I do stuff like
//GUI::bIsConsoleWindowActive = true;
//GUI::unselectWindow();
//GUI::selectedWindow = this;
}
It seems to me you can't add extra things to the construction process without rewriting the constructor and calling the base and passing all the values. This is common throughout my classes.
If your base classes are copyable/moveable and not abstract, you can push the burden of constructing the base class onto the declaration of the derived object. Just accept a base object:
Derived(Base b, int extraMember) :
Base(std::move(b)), extraMember(extraMember)
{
}
Where the declaration becomes this
Derived d{Base{6, 6.0, "name", 'a'}, 4};
That isn't perfect either, since it places certain requirements on your base classes that may not hold. And if it looks like aggregate initialization to you, then that's because this sets out to mimic it.
I'm afraid the only way that can definitely be adapted to any situation, is to spell out a new c'tor like you do now.
It wasn't clear from your question if you are able to change the base class. Assuming you are, you might consider packaging up your base constructor arguments into a struct, which could be forwarded up from the derived constructor.
This is often seen when implementing the builder design pattern, which is one way to solve the telescoping constructor anti-pattern you're describing.
struct FooParts {
int i;
double d;
string s;
char c;
};
class Foo {
public:
Foo(FooParts const & parts) : parts(parts) {}
private:
FooParts parts;
};
class Derived : public Foo {
public:
Derived(FooParts const & parts, bool extra) : Base(parts), member(extra) {}
private:
bool member;
};
I'm not sure, from your question, if you realize that you can call the base constructor from your derived constructor. Using your example:
struct Window
{
Window(Window* parent, vec2 position, vec2 size, const String& name);
};
struct ConsoleWindow : Window
{
ConsoleWindow(Window* parent, vec2 position, vec2 size, const String& name, int otherValue)
: Window(parent, position, size, name)
{
// do something with `otherValue` here.
}
};
See for example here: calling the base class constructor in the derived class constructor
The most straightforward way to do what you described is to use the base class constructor in the initialization list of the derived class:
class Foo
{public:
Foo(int a, double b, std::string name, char somethingElse);
};
class Derived : public Foo
{
public:
Derived::Derived(int a, double b, std::string name, char somethingElse,
int _extraMember, char derivedSpecificThing) :
// Initialization list starts here
Foo(a, b, name, somethingElse),
extraMember(_extraMember)
{
// Do something with derivedSpecificThing etc.
}
private:
const int extraMember;
};
using Foo::Foo only exposes the constructor of the base class as an additional constructor for your derived class. Of course, the constructor of the base class cannot initialize the additional members of the derived class that it knows nothing about.
I have a situation where the top class requires a parameter while none of the child classes does. Some class:
class SuperClass {
public:
SuperClass(const int parameter);
}
And it's child:
class NoParametersInConstructorClass : public SuperClass {
public:
// This will cause an error:
// error: no matching function for call to 'SuperClass::SuperClass(const int)'
NoParametersInConstructorClass() : someText("Hello") {};
private:
std::string someText
}
Now the problem I am facing is that I do have an initializer list, but I find it tedious to rewrite the arguments in all child classes. I am lazy I know. I would like this to work without explicitly defining it:
// Works even though NoParametersInConstructorClass(const int) isn't defined
NoParametersInConstructorClass variable(66);
Of course, if there is no way, there is no way, but is there?
All the derived classes have to pass some value to the base class. It will be easy for the derived classes to have a default constructor in the base class that is in the protected section. You can initialize the member data of the base class that makes sense for the derived classes.
class SuperClass {
public:
SuperClass(const int parameter);
protected:
SuperClass() : SuperClass(10) {} // Delegate to the other constructor.
}
If your problem is that you have a certain set of children, all of which need the SuperClass constructor to be called with the same default argument, all you need to do is to introduce an intermediary with no-argument constructor, which would call the SuperClass::SuperClass with the magic number.
Make the parameter defaulted.
class SuperClass {
public:
SuperClass(const int parameter = DEFAULT);
}
You can then chose to include the constructor in your initialize list, or use the default.
Default value:
class NoParameterChildDefault : public SuperClass
{
public:
NoParameterChildDefault() : someText("Hello") { ... } // uses value DEFAULT.
};
Explicit Value:
class NoParameterChildExplicit : public SuperClass
{
public:
NoParameterChildExplicit() : SuperClass(5), someText("Hello") { ... } // uses value '5'.
};
You may wanna change your default child constructor:
class NoParametersInConstructorClass : public SuperClass {
public:
NoParametersInConstructorClass(int n =0) : SuperClass(n)someText("Hello") {};
private:
std::string someText
}
This way both NoParametersInConstructorClass variable(66); and NoParametersInConstructorClass variable; will work.
To answer your edited question:
You can use the using keyword to make the derived class use the base class constructor:
class NoParametersInConstructorClass : public SuperClass {
public:
using SuperClass::SuperClass;
NoParametersInConstructorClass() : someText("Hello") {};
private:
std::string someText
}
You have to somehow define a constructor for the derived class. You can't just call the base class constructor for the derived class without the using statement.
Let's suppose you can't (or you won't) modify the base class to add a default constructor, you have to call the constructor of the base class in its valid form (with argument) in initialization list of every derived class (like here)
I think the question title may be impossible to get right but, I have a class (lets call it A) which has a constructor that needs a pointer to another class (called B). This would be fine but when I define a class in a third class (called C), then I get this error after putting a variable into the constructor: error: expected identifier before ‘&’ token, then if I remove the & I get: error: ‘ClassA’ is not a type.
For those who can't understand what I just said (I don't blame you), here is an example:
class A
{
private:
int number;
//etc, etc...
};
class B
{
private:
A* ClassAPntr;
public:
B(A* objectPointer)
{
ClassAPntr = objectPointer;
}
};
class C
{
private:
A ClassA;
B ClassB(&ClassA);
};
int main()
{
C classC;
}
You didn't actually declare a constructor for class C. What you meant to do was this:
class C
{
public:
C()
: b(&a) // you have to provide the arguments to B's constructor here
{ }
private:
A a;
B b; // ... not here
};
Just for clarification, when you did composition by adding A classA private object to class C, the compiler accept this step because A has undefined constructor which means the compiler will make a default constructor for you and call it when make A classA.
The problem in class B because it has a non-default user defined constructor which means that compiler should force you to call it in class C through constructor initialization list or through curly braces in c++11, so it becomes your responsibility simply because compiler can't know which address for which class A Object you are going to pass for class B
constructor.
So why did you get this error message : class A is not a type or expected identifier before ‘&’ token?
the answer is that compiler considered B ClassB(&ClassA) a declaration for a private function with name ClassB and a return value of type B class.
and because you never use this function in your class the compiler will never give you any error related to definition. and you will also never use this member function in other functions like main because it is private
to produce the undefined reference error you can modify c class to be like this:
class C
{
private:
A classA;
B classB();
friend B func() ;
};
B func() {
C c;
return c.classB(); // undefined reference Error
}
Im' trying to initialize my "cl" class with that :
class Base
{
Base(int x){}
private :
Base(){}
};
class Test
{
public:
Base cl(12);
};
But I get (with gcc) error: expected identifier before numeric constant
And I don't want to set the "Base" default constructor public
Any "workaround" ?
This is parsed as a function declaration, resulting in an error because the parameter should be a type:
Base cl(12);
You probably want a Base data member, initialized with the value 12:
Base cl{12}; // requires C++11
If you don't have C++11 support, then you can initialize cl in Test's constructor initialization list:
class Test
{
public:
Test() : cl(12) {}
Base cl;
};
Data-members cannot be initialized directly in class through a constructor call. Because of the Most Vexing Parse1, your c1 member is parsed as a function declaration. An incorrect declaration, albeit.
In C++03 and before, you would need to initialize your class through the initializer-list of the constructor:
class Test
{
public:
Test() : cl(21)
// ^^^^^^^^
{};
Base cl;
};
In C++11, this can be easily done through uniform-initialization:
class Test
{
public:
Base cl{21};
// ^^^^^^^
};
Footnote:
1: The most vexing parse is a specific form of syntactic ambiguity resolution in the C++ programming language.... ~ Wikipedia