Child Class reference parent class

Sep 18, 2022 at 10:42am
I am trying to model a car object and I want the car object to have a child engine object but I want the engine object to have a pointer to the car object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
class Car;

class Engine{
    private:
        Car carObject;
    public:
        Engine(Car * car){
            this->carObject = *car;
        }

};

class Car{
    private:
        Engine engine(this);
    public:
        Car(){

        }
};

int main(){
    Car car;
    return 0;
}


But I am getting a bunch of errors.
1
2
3
4
5
6
7
8
9
10
C:\cpp-playground\pointers\this_pointer.cpp:6:13: error: field 'carObject' has incomplete type 'Car'
         Car carObject;
             ^~~~~~~~~
C:\cpp-playground\pointers\this_pointer.cpp:2:7: note: forward declaration of 'class Car'
 class Car;
       ^~~
C:\cpp-playground\pointers\this_pointer.cpp:16:23: error: expected identifier before 'this'
         Engine engine(this);
                       ^~~~
C:\cpp-playground\pointers\this_pointer.cpp:16:23: error: expected ',' or '...' before 'this'

Anything wrong with my modelling?
Sep 18, 2022 at 10:53am
Just to get the terminology right...

A child class inherits from a parent class. This models an is-a relationship.

In your code you're not using inheritance, which is correct because an engine is not a car, but that means you don't have any child or parent classes.

What you have is called "composition", one class containing an instance of another class, which models a has-a relationship. A car has an engine.


shaefayejem wrote:
I want the engine object to have a pointer to the car object

In that case you need to make carObject into a pointer
 
Car* carObject;
and not dereference the car pointer in the constructor.
 
this->carObject = car;

Another problem with your code is that you cannot specify default member initializers using normal parentheses. You have to use = or {}.
 
Engine engine{this};
Last edited on Sep 18, 2022 at 10:56am
Sep 18, 2022 at 11:14am
Ohhh, got it. Thank you for the clear concept explanation about inheritance and composition.

Another problem with your code is that you cannot specify default member initializers using normal parentheses. You have to use = or {}.

I have an additional doubt though.. so I cannot use parenthesis but this works.
When you say you are referring to the "=" then it means dynamic allocation using "new"?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
class Car;

class Engine{
    private:
        Car * carObject;
    public:
        Engine(Car * car){
            this->carObject = car;
        }

};

class Car{
    private:
        Engine engine{this};
        //This does not work
        //Engine engine = new Engine(this); 
    public:
        Car(){

        }
};

int main(){
    Car car;
    return 0;
}


When I use this
Engine engine = new Engine(this)

I am getting below error, based on my understanding, I am passing the "this" car pointer to the Engine
1
2
error: could not convert '(operator new(8), (<statement>, ((Engine*)<anonymous>)))' from 'Engine*' to 'Engine'
         Engine engine = new Engine(this);

Sep 18, 2022 at 11:27am
No, I meant like
 
Engine engine = this;

Although you might want to disable this syntax by marking the constructor as explicit.
1
2
3
explicit Engine(Car * car){
    this->carObject = car;
}
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c46-by-default-declare-single-argument-constructors-explicit

new returns a pointer so you need to use a pointer variable
 
Engine* engine = new Engine(this);
and then you need to use delete when you are finished using the object
 
delete engine;
otherwise you'll have a "memory leak" (using "smart pointers" like std::unique_ptr could help with this).
Last edited on Sep 18, 2022 at 11:32am
Sep 18, 2022 at 11:33am
Thank you.. you are awesome.. learned a lot from this question of mine.
Sep 18, 2022 at 12:46pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Car;

class Engine {
private:
	Car* carObject {};

public:
	explicit Engine(Car* car) : carObject { car } {}
};

class Car {
private:
	Engine engine { this };

public:
	Car() {}
};

int main() {
	Car car;
}


The question now though is - what about copy constructor and assignment?

Sep 18, 2022 at 1:30pm
That can of worms makes one ask: Why should engine know the car that it is in?

Remember, car has an engine. Car will use the engine. Car will use the interface of the engine. What information (about car) does the engine actually need? The car can surely supply that info without handing out this to engine.


PS. Boats and planes have engine too. Engines that must point to cars will not fit.
Last edited on Sep 18, 2022 at 1:33pm
Sep 18, 2022 at 4:34pm
Your Car class as you originally wrote it (if you could construct it) would be infinitely recursive. i.e. C Car has a Engine. That engine has a Car. That Car has a Engine. Ad infinitum.

As keskiverto wrote: WHY do you want to do this? There is no reason that an Engine needs a pointer to it's Car class.
Sep 18, 2022 at 4:35pm
It could be used for reverse look-up. Given an Engine, what Car is it used in? You could have polymorphic classes based upon a base class of say Vehicle. Then Engine could hold a pointer to Vehicle.

However, as Car has Engine (or Vehicle has Engine) I agree about why should Engine have any knowledge about it's use? In practice you'd have say a vector of Car (as it's unlikely you'd have a program that only dealt with one Car!). If you needed to say interrogate which Car has a particular Engine (given say an Engine serial number) then this can be done otherwise.

If you have a circular reference (needing a class to be forward declared), then this often points to a design issue requiring in many cases a re-think.
Topic archived. No new replies allowed.