classes and constructor using strings C++

Personal Contribution:

Obviously there are much easier way to do this code, but for the purpose of learning, it is basic.


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
#include <iostream>
using namespace std;

class name
{
private:
    string first_name;
    string last_name;
 
public:
    name() // default constructor, takes no parameter.
    {
         first_name = "Jack";
         last_name = "Peter";
    }
 
    string get_first_name() { 
        return first_name; 
    }
    string get_last_name() { 
        return last_name; 
    }
    string get_result() { 
        string name = first_name + " " + last_name;
        return name;
    }
};
 
int main()
{
    name use; 
    cout << use.get_first_name() << " " << use.get_last_name() << '\n' << endl;
    // or
    cout << use.get_result() << endl;
 
    return 0;
}




Jack Peter

Jack Peter
I am open to new ideas. Feel free to mention suggestions, I am sure someone including myself will find it helpful.
Kourosh23 wrote:
Feel free to mention suggestions

Here are a few

1
2
3
4
5
    name() // default constructor, takes no parameter.
    {
         first_name = "Jack";
         last_name = "Peter";
    }

This first constructs both members as empty string objects, only to destroy them in the assignment operators.
Followng Core Guidelines rule C.49: Prefer initialization to assignment in constructors https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-initialize this should be:

1
2
3
    name(): first_name{"Jack"}, last_name{"Peter"}
    { // constructor body is generally empty
    }


and going further, your initial values are constants, so C.48: Prefer in-class initializers to member initializers in constructors for constant initializers https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-in-class-initializer

1
2
3
4
5
class name
{
private:
    string first_name = "Jack";
    string last_name = "Peter";

and drop that constructor entirely.

Next,

1
2
3
    string get_first_name() { 
        return first_name; 
    }

This member function makes no changes to the class, it has to be const:
Con.2: By default, make member functions const https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rconst-fct

1
2
3
    string get_first_name() const { 
        return first_name; 
    }


but, more significantly, your getters are trivial, and you could simplify this further:
C.131: Avoid trivial getters and setters https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rh-get

also applying C.2: Use class if the class has an invariant; use struct if the data members can vary independently https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-struct
and C.4: Make a function a member only if it needs direct access to the representation of a class https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-member
and for good measure SL.io.50: Avoid endl https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rio-endl

also restoring the #include you missed and applying a couple more style tweaks..


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <string>
struct name
{
    std::string first_name = "Jack";
    std::string last_name = "Peter";
};

std::string get_result(const name& n) { 
    return n.first_name + " " + n.last_name;
}
 
int main()
{
    name use; 
    std::cout << use.first_name << " " << use.last_name << "\n\n";
    // or
    std::cout << get_result(use) << '\n';
}


now of course if you don't want to make it possible to alter the strings once the object is created, then you have an invariant and it has to be a class with getters:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <string>
class name
{
    std::string first_name = "Jack";
    std::string last_name = "Peter";
 public:
     std::string get_first_name() const { return first_name; }
     std::string get_last_name() const { return last_name; }
};

std::string get_result(const name& n) { 
    return n.get_first_name() + " " + n.get_last_name();
}
 
int main()
{
    name use; 
    std::cout << use.get_first_name() << " " << use.get_last_name() << "\n\n";
    // or
    std::cout << get_result(use) << '\n';
}
Last edited on
@Cubbi, all I am speechless. Thank you so much for clarifying stuff. I am in the process of learning and implementing C++, so new ideas and explanations are always helpful. :)

Just a suggestion to your final sample code. It is possible to send an object as pass by reference like the code you wrote in main function:

1
2
3
4
5
int main(){
name use;
std::cout<< get_result(use) << endl;
return 0;
}


and then

1
2
3
std::string get_result(const name& n) { 
    return n.get_first_name() + " " + n.get_last_name();
}


However, it is not necessary. Inside the function get_result, you can create another object, so what I mean is:

1
2
3
4
std::string get_result() { 
    const name n;
    return n.get_first_name() + " " + n.get_last_name();
}



I don't understand the reason behind your purpose for sending the object n by pass by reference . Could you please explain about that ?
Last edited on
CppCoreGuidelines wrote:
Avoid trivial getters and setters

I think it's important to mention when this advice does not apply.

The traditional argument against exposing public members is that doing so breaks encapsulation. Public data members are fine as long as they're unlikely to change. Ideally, nobody knows about the inside of a class -- this helps keep code flexible by ensuring that other modules' use of the interface (the public stuff) is independent of the details inside the class. If these two things are independent, you can change the details without having to change the interface --- i.e., your changes can be local to just one place.

If you expose public members, you are exposing the implementation: the implementation is the interface. That means that any changes to the implementation will necessitate changes all over your program. In large programs, this can be difficult.

You should read this article by Scott Meyers, who explains this better than I:
http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197
The main point of the article is worth knowing, too.

My point is that the advice "avoid trivial getters and setters" should not be simplified to "never write trivial getters and setters". I would argue that if you must simplify the rule, the opposite is a better choice -- if you must always do one or the other, always write them.
Kourosh23 wrote:
Inside the function get_result, you can create another object

Well yes, since every instance of your object is just another copy of the same constant, you can do that too, but then the whole thing degenerates to a bunch of static constants.

mbozzi wrote:
I think it's important to mention when this advice does not apply.

Guidelines have their own discussion in that link (and a further discussion in github issues)
If you ask me, a trivial setter is always a gross design error, and trivial getters are useful when they are needed to expose sufficient state for testing, but it's just personal work experience.
Last edited on
Topic archived. No new replies allowed.