LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   C++ Initialize Class Member Variables of Another Class Type (https://www.linuxquestions.org/questions/programming-9/c-initialize-class-member-variables-of-another-class-type-874667/)

mirlin510 04-12-2011 08:20 PM

C++ Initialize Class Member Variables of Another Class Type
 
So I'm wondering how I can make a constructor for a class contain other class members.

Here's my constructor:

Course::Course()
{
courseCode = "";
section = "";
DaysOfWeek* daysMeets = new DaysOfWeek();
TimeInterval* timeMeets = new TimeInterval();
instructorName = "";
}

My private member variables for Course is:
string courseCode;
string section;
DaysOfWeek daysMeets;
TimeInterval timeMeets;
string instructorName;

Will my daysMeets private variable be initialized (which is what I want) or will it just create it's own NEW DaysOfWeek object that overrides my private member variable. I have a feeling it's the second, so my question is: what is the proper way to initialize a private member variable that is of another class type?

Unfortunately I can't test this for some time, and it's really bothering me. I've searched all over for the answer, it compiles of course, but I doubt it's doing what I want.

mirlin510 04-12-2011 08:28 PM

Also, this compiles, but I'm not sure if this is what I'm looking for either, but it does seem much more like what I want:

daysMeets = DaysOfWeek();
timeMeets = TimeInterval();

graemef 04-12-2011 11:07 PM

daysMeets is a pointer to an object. So what you want is:
Code:

daysMeets = new DaysOfWeeks();
this will make your private member variable point to the newly created object. It will then hand over responsibility to the DaysOfWeeks class (specifically the constructor) to initialise this object.

What you had:
Code:

DaysOfWeeks * daysMeets = new DaysOfWeeks();
will hide the member variable with a local variable (which will then get lost once the constructor completes) and provide you with an object that you can no longer acess.

dwhitney67 04-13-2011 06:33 AM

@ the OP --

Please be advised that the proper place to initialize class member data is not in the body of the constructor, but prior to it.
Code:

class Foo
{
public:
  Foo();

private:
  int value;
};


Foo::Foo()
  : value(0)    // initialize member data in this section
{
  // once this point is reached, the object should hopefully be fully constructed.
}

For your code, it is unnecessary to initialize std::string objects since these already have a default constructor; thus the following would be appropriate:
Code:

Course::Course()
  : daysMeets(new DaysOfWeek),
    timeMeets(new TimeInterval)
{
}


Ramurd 04-13-2011 07:25 AM

You can also make multiple constructors by overloading them:

Code:

Course::Course()
{
  courseCode = "";
  section = "";
  DaysOfWeek* daysMeets = new DaysOfWeek();
  TimeInterval* timeMeets = new TimeInterval();
  instructorName = "";
}

Course::Course(DaysOfWeek *meets)
{
  courseCode = "";
  section = "";
  DaysOfWeek *daysMeets = meets;
  TimeInterval *timeMeets = new TimeInterval();
  instructorName = "";
}

Course::Course(DaysOfWeek *meets, TimeInterval *times)
{
  courseCode = "";
  section = "";
  DaysOfWeek *daysMeets = meets;
  TimeInterval *timeMeets = times;
  instructorName = "";
}

You can then construct your class like this:
new Course(days, meets);
or
new Course(days);
or
new Course();

in case you constructed the other classes already, you can use their data in your constructor.

johnsfine 04-13-2011 07:42 AM

Quote:

Originally Posted by mirlin510 (Post 4322803)
My private member variables for Course is:
string courseCode;
string section;
DaysOfWeek daysMeets;
TimeInterval timeMeets;
string instructorName;

I think some of those answering earlier might have missed the detail that daysMeets and timeMeets are not pointers.

You have clearly implied that DaysOfWeek and TimeInterval both have default constructors. That means you don't need to initialize daysMeets nor timeMeets. The compiler takes care of that for you, just as it takes care of initializing the strings for you. So your Course constructor doesn't need to do anything at all.

Assuming Course is something like
Code:

class Course {
public:
  Course();
private:
  string courseCode;
  string section;
  DaysOfWeek daysMeets;
  TimeInterval timeMeets;
  string instructorName;
};

All you actually need for the constructor is
Code:

Course::Course() {}
A good constructor might be:
Code:

Course::Course() :
  courseCode(),
  section(),
  daysMeets(),
  timeMeets(),
  instructorName()

  {}

Each one of the lines in blue is individually optional (subject to having the , on all but the last one included and removing the : if you remove all those lines).

Those lines remind anyone reading your code that the default constructor is used for each of those members. If you leave the line out, the compiler still knows the default constructor is used.

If you wanted to use a non default constructor for any one of those members, you would fill in the constructor parameters in the ().

Quote:

Originally Posted by mirlin510 (Post 4322808)
Also, this compiles, but I'm not sure if this is what I'm looking for either, but it does seem much more like what I want:

daysMeets = DaysOfWeek();
timeMeets = TimeInterval();

That would compile, and would get the job done. But it is doing a lot of throw away work:
The compiler inserts a call to the default constructor for each of those members because you didn't specify their construction. Then you specify that a temporary object of the same type be created with another call to the default constructor, then copied over the already initialized member with a call to the assignment operator. (The compiler might optimize away some of that extra nonsense, but you still shouldn't have coded it in the first place).

mirlin510 04-13-2011 08:02 AM

Quote:

Originally Posted by johnsfine (Post 4323259)
I think some of those answering earlier might have missed the detail that daysMeets and timeMeets are not pointers.

You have clearly implied that DaysOfWeek and TimeInterval both have default constructors. That means you don't need to initialize daysMeets nor timeMeets. The compiler takes care of that for you, just as it takes care of initializing the strings for you.

Assuming Course is something like
Code:

class Course {
public:
  Course();
private:
  string courseCode;
  string section;
  DaysOfWeek daysMeets;
  TimeInterval timeMeets;
  string instructorName;
};

A good constructor might be:
Code:

Course::Course() :
  courseCode(),
  section(),
  daysMeets(),
  timeMeets(),
  instructorName()

  {}

Each one of the lines in blue is individually optional (subject to having the , on all but the last one included and removing the : if you remove all those lines).

Those lines remind anyone reading your code that the default constructor is used for each of those members. If you leave the line out, the compiler still knows the default constructor is used.

If you wanted to use a non default constructor for any one of those members, you would fill in the constructor parameters in the ().

This is much more of what I'm looking for, but what if I overloaded a constructor and want to make my own DaysOfWeek/TimeInterval object equal to it and set my local variable to that new object? I guess I should not have used a default constructor as an example.

I currently have something like this in the other constructor:

Course::Course(string aCourseCode, string aSection, DaysOfWeek&
aDaysMeets, TimeInterval& aTimeMeets, string aInstructorName)
{
courseCode = aCourseCode;
section = aSection;
daysMeets = DaysOfWeek(aDaysMeets.get());
timeMeets = TimeInterval(aTimeMeets.getStart(),aTimeMeets.getEnd());
instructorName = aInstructorName;
}

Quote:

That would compile, and work get the job done. But it is doing a lot of throw away work:
The compiler inserts a call to the default constructor for each of those members because you didn't specify their construction. Then you specify that a temporary object of the same type be created with another call to the default constructor, then copied over the already initialized member with a call to the assignment operator. (The compiler might optimize away some of that extra nonsense, but you still shouldn't have coded it in the first place).
What is the proper way to do this for a non-default constructor then if I have my own DaysOfWeek object being passed into the constructor?

johnsfine 04-13-2011 08:52 AM

Quote:

Originally Posted by mirlin510 (Post 4323279)
Course::Course(string aCourseCode, string aSection, DaysOfWeek&
aDaysMeets, TimeInterval& aTimeMeets, string aInstructorName)
{
courseCode = aCourseCode;
section = aSection;
daysMeets = DaysOfWeek(aDaysMeets.get());
timeMeets = TimeInterval(aTimeMeets.getStart(),aTimeMeets.getEnd());
instructorName = aInstructorName;
}

The better way to do that is:
Code:

Course::Course(string const& aCourseCode,
              string const& aSection,
              DaysOfWeek& aDaysMeets,
              TimeInterval& aTimeMeets,
              string const& aInstructorName
    ) :
    courseCode( aCourseCode ),
    section( aSection ),
    daysMeets( aDaysMeets.get() ),
    timeMeets( aTimeMeets.getStart(),aTimeMeets.getEnd() ),
    instructorName(aInstructorName )
    {}

Comments:

1) Passing strings by const& is generally better than by value (and definitely better in this example), so I made that change. But this change is independent of the overall change in the structure of the constructor.

2) The sequence of the initialization list between the ) : and the {} should match the sequence that the members are declared in the definition of the class. I don't know what sequence you declared them in, so I just kept the sequence from your version of the constructor. Note the sequence of parameters to your constructor is whatever you want. The compiler does not relate it to the sequence of members initialized from those parameters.

3) I see you passed two objects by & rather than const&. I didn't change that because I don't know whether your get() methods are const methods. But it looks like a situation where the get() methods should have been const and the parameters passed to Course() should have been const&.

4) Your code makes me wonder what DaysOfWeek(aDaysMeets.get()) is doing. It looks like the intent is to copy the object. Is there a good reason that is not done with a direct copy? In your form of the constructor that would be
daysMeets = aDaysMeets;
In my form of the constructor, that would be
daysMeets( aDaysMeets ),

5) As I explained before, your version of the code looks like it would work as long as DaysOfWeek and TimeInterval have default constructors. But your code does a lot of excess work: calling the default constructor that you don't actually want, then making a temporary object with the correct constructor, then copying the temporary object using the assignment operator. In a well written project, I expect DaysOfWeek and TimeInterval would not have default constructors, because neither object seems to make any sense prior to having contents. If they don't have default constructors then your form of the Course() constructor can't work. That is why it is better to use the form dwhitney67 suggested and I have been repeating. This form avoids the implied call to default constructors that you don't want invoked.

mirlin510 04-13-2011 08:58 AM

Quote:

Originally Posted by johnsfine (Post 4323333)
The better way to do that is:
Code:

Course::Course(string const& aCourseCode,
              string const& aSection,
              DaysOfWeek& aDaysMeets,
              TimeInterval& aTimeMeets,
              string const& aInstructorName
    ) :
    courseCode( aCourseCode ),
    section( aSection ),
    daysMeets( aDaysMeets.get() ),
    timeMeets( aTimeMeets.getStart(),aTimeMeets.getEnd() ),
    instructorName(aInstructorName )
    {}


Excellent! Thank you very much for your help.

mirlin510 04-13-2011 11:46 AM

Quote:

Your code makes me wonder what DaysOfWeek(aDaysMeets.get()) is doing. It looks like the intent is to copy the object. Is there a good reason that is not done with a direct copy? In your form of the constructor that would be
daysMeets = aDaysMeets;
In my form of the constructor, that would be
daysMeets( aDaysMeets ),
I should probably be doing a direct copy, yes. I have changed my code to initialize before the brackets as well.


All times are GMT -5. The time now is 11:18 PM.