Class Questions

Pages: 1234
I tried your recommendations but Person(std::string n = "Name", DateOfBirth d) : name(n), DOB(d) {} will not compile and Person(std::string n = "Name", int d = 1, int m = 1, int y = 1) : name(n), DOB(d, m, y) {} is giving me warnings.
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <string>

using namespace std;

class DateOfBirth
{
    public:
        DateOfBirth(int d, int m, int y)
        : day(d), month(m), year(y) {}

        void displayDOB()
        {
            cout << day << "/" << month << "/" << year;
        }

    private:
        int day;
        int month;
        int year;
};

class Person
{
    public:
        Person(string n = "Name", int d = 1, int m = 1, int y = 1)
        : name(n), DOB(d, m, y) {}

        string getName()
        {
            return name;
        }

        DateOfBirth DOB;

    private:
        string name;
};

int main()
{
    Person person1;

    cout << person1.getName() << "'s date of birth is ";
    person1.DOB.displayDOB();

    return 0;
}

Warnings:

||=== Build: Debug in composition (compiler: GNU GCC Compiler) ===|
F:\Program Files\Programming\C++ Programs\composition\main.cpp||In constructor 'Person::Person(std::string, int, int, int)':|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|37|warning: 'Person::name' will be initialized after [-Wreorder]|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|34|warning:   'DateOfBirth Person::DOB' [-Wreorder]|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|26|warning:   when initialized here [-Wreorder]|
||=== Build finished: 0 error(s), 3 warning(s) (0 minute(s), 0 second(s)) ===|
||=== Run: Debug in composition (compiler: GNU GCC Compiler) ===|

Do you know how to fix these warnings?
Person(std::string n = "Name", DateOfBirth d) : name(n), DOB(d) {} will not compile
Remove default value from n. Or add it to DOB, or swap order of name and dob.

Do you know how to fix these warnings?
Yes, change order of initialization in constructor:
Person(string n = "Name", int d = 1, int m = 1, int y = 1) : DOB(d, m, y), name(n) {}
I can get the second solution to work but not the first. Why does the initialisation order matter?

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <string>

using namespace std;

class DateOfBirth
{
    public:
        DateOfBirth(int d, int m, int y)
        : day(d), month(m), year(y) {}

        void displayDOB()
        {
            cout << day << "/" << month << "/" << year;
        }

    private:
        int day;
        int month;
        int year;
};

class Person
{
    public:
        Person(string n = "Name", DateOfBirth d)
        : DOB(d), name(n) {}

        string getName()
        {
            return name;
        }

        DateOfBirth DOB;

    private:
        string name;
};

int main()
{
    Person person1("Person", 2, 3, 4);

    cout << person1.getName() << "'s date of birth is ";
    person1.DOB.displayDOB();

    return 0;
}


||=== Build: Debug in composition (compiler: GNU GCC Compiler) ===|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|75|error: default argument missing for parameter 2 of 'Person::Person(std::string, DateOfBirth)'|
F:\Program Files\Programming\C++ Programs\composition\main.cpp||In function 'int main()':|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|91|error: no matching function for call to 'Person::Person(const char [7], int, int, int)'|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|91|note: candidates are:|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|75|note: Person::Person(std::string, DateOfBirth)|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|75|note:   candidate expects 2 arguments, 4 provided|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|72|note: Person::Person(const Person&)|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|72|note:   candidate expects 1 argument, 4 provided|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|72|note: Person::Person(Person&&)|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|72|note:   candidate expects 1 argument, 4 provided|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

How can I get this to work?
Initialization order does not actually matter and this is why warning is issued: you might think that order in initialization list matters, but it does not. Members are always initializated in order of their declaration in class. So it is a good idea to have them in same order in initialization list to make sure that there would not any surprises.

About first one:
MiiNiPaa wrote:
Remove default value from n.
Last edited on
Thanks for the explanation.

I removed the default value but I'm still getting errors:

||=== Build: Debug in composition (compiler: GNU GCC Compiler) ===|
F:\Program Files\Programming\C++ Programs\composition\main.cpp||In function 'int main()':|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|91|error: no matching function for call to 'Person::Person(const char [5], int, int, int)'|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|91|note: candidates are:|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|75|note: Person::Person(std::string, DateOfBirth)|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|75|note:   candidate expects 2 arguments, 4 provided|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|72|note: Person::Person(const Person&)|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|72|note:   candidate expects 1 argument, 4 provided|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|72|note: Person::Person(Person&&)|
F:\Program Files\Programming\C++ Programs\composition\main.cpp|72|note:   candidate expects 1 argument, 4 provided|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

no matching function for call to 'Person::Person(const char [7], int, int, int)'
You are trying to call constructor taking name and 3 ints, but implemented only one taking name and DateOfBirth.
The thing is though, in the call to the constructor I have to supply values for the day, month and year. Supplying three ints is I think unavoidable.
 
Person person1("Person", 2, 3, 4);


EDIT:
Is this the only way to get around the problem?
1
2
DateOfBirth dob(2,2,2);
Person person1("Name", dob);
Last edited on
No, you can either create constructor taking 3 ints, or pass a temporary: http://ideone.com/kgvHX5
Calling the constructor that way works, but since it's a 'temporary' pass, does that mean the members aren't permanently set to the values you pass?
No, they are set. Temporary refers to value you pass: you are creating a temporary object with data you enter, pass it to Person constructor, constructor copies data from passed object to Person member, then passed object is destroyed as it is only temporary. But data is already extracted from it and safely stored, so there is no danger that it will disappear.
Okay, thanks for clearing that up. That's a nice technique to know about. Can you pass as many temporary objects as you want?
Yes, just be wary about cost of creating and destroying them. Often it is better to take them by const reference instead of by value to mitigate amount of copies. (And sometimes it is better to take it by value and move in place, after you learn move semantics)
Is it good or bad design to set up classes in such a way as to have a base class that you derive other classes from, but never create an object of that base class? For example, you may have a base class called Class to contain generic information, such as names and stats, that all derived classes, such as Warrior or Mage, will use.

With inheritance like this, it wouldn't really make sense to create objects of type Class, but rather it would be better to create objects of the more well defined derived classes, Warrior and Mage.

Is this good or bad design?
Last edited on
Sounds like Abstract Base Class (ABC). Which is legitimate design choice.
https://isocpp.org/wiki/faq/abcs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

using namespace std;

class MyClass
{
    public:
        void print()
        {
            cout << "Hello." << endl;
        }
};

int main()
{
    MyClass *pointer = new MyClass();

    pointer->print();

    return 0;
}


In this example, is line 16 referred to as instantiating an object pointer of a class, instantiating a pointer of type class, or something else?

When should objects be created like this?

What advantages and disadvantages are there to dynamically creating objects this way?
Bump.
In line 16, you're creating an instance of the class on the heap, by dynamic allocation.

You do this when:

1) You want to control the lifetime of the object, rather than having it destroyed when it falls out of scope

2) You don't know how much memory you'll need at compile-time, only at run-time. Typically, this is when allocating an array whose size you don't know at compile-time, although these days, you're better off using STL collection classes that manage their own memory dynamically.
Thank you. Would you still refer to the pointer as an object of the class?

Should you try to minimise the use of class pointers and instead stick to solid objects whenever possible?
The pointer is usually referred to as having the type "pointer-to-className". Which is why it is C++ style to write MyClass* a because a is of the type pointer-to-MyClass, whereas it is C style to write MyClass *a because when you dereference a (via *a), you get an object of type MyClass. You really shouldn't use pointers in C++ if you don't need them. Dynamic allocation and heap objects in general are slower than stack allocation and stack objects, so only use them if you need something to outlive its scope. Also, passing by pointer is basically pointless since passing by reference.
Thank you. Would you still refer to the pointer as an object of the class?

Not if you're being precise - which is often important. It's a pointer to an object of the class.

Should you try to minimise the use of class pointers and instead stick to solid objects whenever possible?

You should instantiate objects directly on the stack, if that's what you need, and you should dynamically allocate when that's what you need. In general, if you can get away with creating things on the stack, then do.

There are more uses for pointers than just for dynamic allocation. As shadowmouse says, modern C++ provides alternatives that are almost always better than using raw C-style pointers. However, those are advanced topics - as a beginner, there's nothing at all wrong with learning how to use pointers first, before moving on to the better alternatives such as references and smart pointers.
Last edited on
Pages: 1234