Reputation: 2903
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?
Upvotes: 18
Views: 61229
Reputation: 5528
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!
Upvotes: 16
Reputation: 56539
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....
Upvotes: 17
Reputation: 689
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.
Upvotes: 0
Reputation: 68907
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.
Upvotes: 3