18

I am not sure that I am using the right terminology, but question is how do I properly make a constructor that takes a string in as a parameter?

I am used to having a const char * in the constructor instead of strings.

Normally I would do something like this:

Name(const char* fName, const char* lName) : firstName(0), lastName(0) { char * temp = new char [strlen(fName) + 1]; strcpy_s(temp, strlen(fName) + 1, fName); firstName = temp; char * temp2 = new char [strlen(lName) + 1]; strcpy_s(temp2, strlen(lName) + 1, lName); lastName = temp2; } 

What if the constructor is this:

 Name(const string fName, const string lName) { } 

Do I still do base member initialization? do I still need to use string copy in the base of the constructor?

4 Answers 4

17

Use std::string and initializer lists:

std::string fName, lName; Name(string fName, string lName):fName(std::move(fName)), lName(std::move(lName)) { } 

In this case, you don't need to use terribly bare pointers, you don't need allocate memory, copy characters and finally de-allocate. In addition, this new code has chances to take advantages of moving rather than copying since std::string is movable. Also it's useful to read this.

And so on....

Sign up to request clarification or add additional context in comments.

5 Comments

Why wouldn't I initialize to 0 as I would with char *?
@Sarah: Because std::string objects are not supposed to be 0, because it are objects (not pointers). They are empty, like in the sense of "".
@Sarah: In addition to Martijn's comment, above code is initializing and copying simultaneously. You don't need anything else.
The above code could actually incur at least 2 copies unless you explicitly specify std::move in the initializer list. Also, this is not a good example of move semantics because you still only get 1 copy; exactly the same as if you had used a reference.
@CoffeeandCode: Yes, you are right, I don't know why I forgot to use std::move
16

I see that you have already accepted an answer but I would like to expand upon the answers.

As deepmax said, if you pass by value you can write your constructor to take advantage of "move semantics". This means instead of copying data, it can be moved from one variable to another.

Written like so:

class Name{ public: Name(std::string var): mem_var(std::move(var)){} std::string mem_var; }; 

Which seems like a good idea, but in reality is no more efficient than the copy constructor

class Name{ public: Name(const std::string &var): mem_var(var){} std::string mem_var; }; 

The reason this is, is because in the general use case that looks like this:

auto main() -> int{ Name name("Sample Text"); } 

only one copy will ever get made either way (see copy elision), and in the other case of

auto main() -> int{ std::string myname = "Hugh Jaynus"; Name name(myname); } 

2 copies will be made in the 'efficient' pass-by-value move semantics way!

This is a good example of when the copy constructor (or pass-by-reference) should be used, not an example against it.


On the contrary...

If you write an explicit constructor that makes use of move semantics you could get an efficient solution no matter the circumstance.

Here is how you might write out a name class definition with both constructors:

class Name{ public: Name(const std::string &first_, const std::string &last_) : first(first_), last(last_){} Name(std::string &&first_, std::string &&last_) // rvalue reference : first(std::move(first_)), last(std::move(last_)){} std::string first, last; }; 

Then when you use the class the more efficient path should be taken.

If we go back to our examples we can rewrite them to make use of the best or most efficient constructor:

int main(){ // pass by reference best here Name myname("Yolo", "Swaggins"); // move most efficient here // but never use 'first' and 'last' again or UB! std::string first = "Hugh", last = "Jaynus"; Name yourname(std::move(first), std::move(last)); } 

Never just take for granted that one solution is better than all others!

2 Comments

I see what you did there 🤣
Where are two copies in case of pass-by-value constructor and ``` auto main() -> int{ std::string myname = "Hugh Jaynus"; Name name(myname); } ```? One copy is from myname variable to constructor argument, which is then moved to the class member. Also consider this article which explains the topic better than I could do in a comment: cpptruths.blogspot.com/2012/03/…
3

I'm used to do this:

std::string fName; std::string lName; Name(const std::string &fName, const std::string &lName) : fName(fName), lName(lName) { } 

Using the references saves the work of copying the strings to a new object on the stack, it will just pass the reference to the existing string. Once you are assigning them to the class members, they will get copied.

10 Comments

Why you don't pass them by value if you concern efficiency?
but what if I'm not using references to strings in my parameters?
@MM: I wrote that in my answer: because they won't get copied by passing them to the constructor. The constructor will have to copy them as well, in every case (with or without references).
@Sarah: Well, then you either stick with your current situation or change it to const references.
@MartijnCourteaux: Move semantics (C++11) say something else, read this article.
|
0

if you want to keep const char * as your constructor input types do this.

std::string fName; std::string lName; Name(const char *_fName, const char *_lName) : fName(_fName), lName(_lName) { } 

You can construct a std::string from a const char.

1 Comment

Identifiers starting with a leading underscore are reserved by the compiler, you should have them trailing.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.