Returning a pointer to char

I have a class that looks like:

class xxxx

private:
char * firstname[20];


public:

const char* getFirstName();



When constructing the "getFirstName" member function, I can't just do "return *firstname"

What is the proper syntax? Is a better design to just change it into "const string getFirstName();"?
Ignoring the issue of getFirstName for a moment:

firstname is an array of 20 pointers-to-char. I don't think that's what you intended.

First off, char * firstname[20]; is an array of 20 pointers, not an array of 20 characters. Get rid of the *.
 
char firstname[20];


I can't just do "return *firstname"

Again, get rid of the *.
 
return firstname;


Is a better design to just change it into "const string getFirstName();"?

Yes.

Last edited on
Is a better design to just change it into "const string getFirstName()
Not quite.

This creates a separate const string each time you call it. If you're returning a separate string, then why say that the caller can't change it?

On the other hand, if someone just wants to read the first name, then why return a separate string at all?

And this won't work:
1
2
3
4
5
6
void f(const XXX &instance)
{
    string first;
    ...
    first = instance.getFirstName();   // Calling non-const method of const object
}


A much more common method is:
1
2
3
4
private:
string firstName;
public:
const string &getFirstName() const;

firstName is a string, so you don't have to worry about overflowing a buffer.

getFirstName() returns a const reference to the string. By returning a reference, you aren't making a copy of the string. By making the reference const, you prevent the caller from changing the underlying string (at least they can't without some casting gymnastics). Finally, my making the method itself const, you say that it can be called on a const object.
Here are four different versions (and more variations are possible)
Think about it, and choose one that is the most appropriate for the specific use case.
Hint: when in doubt, favour xxxx_3

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
49
50
51
52
53
54
55
56
57
58
59
60
class xxxx_1
{
    // may not be so good; the fixed size for a name is not very flexible
    // good: standard layout type, trivially copyable etc.
    static constexpr std::size_t NAMESZ = 20 ;
    char fname[NAMESZ] {} ;
    // ...

    public:
        // not so good; unencapsulated interface exposes the implementation
        //              (change in implementation may break the interface)
        constexpr const char* first_name() const { return fname ; }

        // ...
};

class xxxx_2
{
    // may not be so good; the fixed size for a name is not very flexible
    // good: standard layout type, trivially copyable etc.
    static constexpr std::size_t NAMESZ = 20 ;
    char fname[NAMESZ] {} ;
    // ...

    public:
        // good; encapsulated interface does not expose the implementation
        std::string first_name() const { return fname ; }

        // ...
};

class xxxx_3 // favour this as the default choice
{
    // good, flexible size for name
    std::string fname ;
    // ...

    public:
        // good; encapsulated interface does not expose the implementation
        std::string first_name() const { return fname ; }

        // ...
};

class xxxx_4
{
    // good, flexible size for name
    std::string fname ;
    // ...

    public:
        // not so good, un-encapsulated interface exposes the implementation
        //              (change in implementation would break the interface)
        // however, the performance would be better (avoid copying the string)
        //              (in general, treat performance as a constraint rather than a goal;
        //               so do this if and only if this performance improvement is a requirement)
        const std::string& first_name() const { return fname ; }

        // ...
};
Topic archived. No new replies allowed.