I create a class named Employee, in private, I have a Name as a string . here is my class declaring:
class Employee
{
string Name;
public:
Employee();
void SetName(string);
void StringToEmployee(string);
~Employee();
}
this is definition of StringToEmployee(string) method:
void Employee::StringToEmployee(string s)
{
char *first = s, *end = s+strlen(s), *last = NULL;
last = find(first, end, ',');
string temp(first, last- first);
SetName(temp);
}
The error occurs when I debug to the line string temp(first, last- first), it's seem to the compiler does not allow me to construct a new string in method. cause I have also changed into string temp; then temp.assign(first, last-first). the error still remain. How could I create a new string in a method?
You should be using iterators and taking advantage of the features of the standard library, rather than raw pointers and C-style string functions. Not only will this give you more idiomatic and easier to understand C++ code, but it will also implicitly resolve many of your errors.
First, the implementation of StringToEmployee should be rewritten as follows:
void Employee::StringToEmployee(std::string s)
{
const std::string temp(s.begin(),
std::find(s.begin(), s.end(), ',');
SetName(temp);
}
But since you are not modifying the s parameter and do not need a copy of it, you should pass it by constant reference:
void Employee::StringToEmployee(const std::string& s)
{
const std::string temp(s.begin(),
std::find(s.begin(), s.end(), ',');
SetName(temp);
}
Also, you should consider redesigning your Employee class. Currently, you have a default constructor that creates an invalid Employee object, and then you have member functions that allow you to turn that invalid Employee object into a valid one by settings its members. Instead, you could have a constructor that did all of this initialization for you, in one step. Not only would your code be cleaner and easier to understand, but it would be more efficient, too!
Perhaps something like:
class Employee
{
std::string Name; // name of this employee
public:
Employee(const std::string& name); // create Employee with specified name
void SetName(const std::string& newName); // change this employee's name
~Employee();
};
Employee::Employee(const std::string& name)
: Name(s.begin(), std::find(s.begin(), s.end(), ','))
{ }
void Employee::SetName(const std::string& newName)
{
Name = std::string(s.begin(), std::find(s.begin(), s.end(), ','));
}
Employee::~Employee()
{ }
A couple of quick notes:
You'll see that I always explicitly write out std:: whenever I use a class from the standard library's namespace. This is a really good habit to get into, and it's not really that hard to type an extra 5 characters. It's particularly important because using namespace std; is a really bad habit to get into.
I pass objects (like strings) that I don't need to modify or have a copy of inside of the method by constant reference. This is both easier to reason about, and also potentially more efficient (because it avoids unnecessary copies).
Inside of the constructor, I have used what may appear to be a funny-looking syntax, involving a colon and some parentheses. This is called a member initialization list, and it's something you should get used to seeing. It's the standard way for a class's constructor to initialize its member variables.
For some reason you want to assing std::string to char*.
Judging from other your code, you want to work with raw char array, so, you need to put correct pointers to first and last like this:
char *first = &s[0], *end = (&s[0]) + strlen(s.c_str()), *last = NULL;
And this part:
string temp(first, last- first);
is incorrect, because last - first is pointer, and, as I understand, you want to use std::string(const char*, size_t) constructor. But instead, you are using iterator-based constructor and system is correctly dying, because first pointer is larger, than second one.
As you see, your method is error-prone. I recommend re-do this part of code, using iterators, like this:
void Employee::StringToEmployee(string s)
{
auto found = find(s.begin(), s.end(), ',');
string temp(s.begin(), found);
SetName(temp);
}
Related
First i want to introduce my situation :
I have write some classes that has char* pointer as private class member.And also this project has GUI, so when click buttons,some functions may execute more than one time.Those classes are designed single class in project.But some functions of them can execute more than one time.Then I found my project has memory leak.
so i want to ask the following questions:
how to design the set function?
how to design the other functions that use the char* member variable?
how to design the class operator= function?
for example:
class A:
{
public :
setStr(char * s){//need new or just use =?};
A & operator=(const A& other){//also need new?};
manyTimesFunctions(char * other)
{
//need to use chars other to assignment str
//how to carefully use new to avoid memory leak?
//other may be another class's locality none const variable
}
private:
char * str;
}
So ,the project only init class A once,but may use setStr and manyTimesFunctions many times.
May be the answer:
I think i have found what i need to take care of:copy that class,that answers are really useful to me.
Just use std::string. It takes care of memory management for you. The member declaration then looks like
std::string str;
and the setter function looks like
void setStr( char const* s ) { str = s; }
Where you want to use the string and need a char const*, just write str.c_str().
With use of standard library types like std::string, and no manual dynamic allocation, you generally don't need to be concerned about operator=: the compiler-generated copy assignment works nicely.
By the way, it's generally a good idea to decide on some naming convention for member variables. Common ones for C++ include str_, mStr, and my_str. The underscore suffix is perhaps the most common one, but don't use a leading underscore like _str, because although technically allowed it conflicts with the conventions for implementation defined names (e.g. leading underscore is not allowed for identifiers in the global namespace).
I am not 100% sure what you are trying to do. However, since char* is a pointer you may be able to simply pass around the references.
char* operator=(char* s) { str = s; }
Just know that then if you modify value in your function it will modify the place you copied it from
If the char* needs to actually be a clone, so that it does not modify the original value. You first need to obtain the length of the char*.
This can be done with this function
unsigned Length(char* s)
{
unsigned I = 0;
while( *(s+I) != '\0')
I++;
return I;
}
The a new string can be created as follows
str = new char[LENGTH];
At that point you can copy the string over term by term
for(I = 0 ; I < LENGTH; I++)
{
str[I] = s[I];
}
Finally to avoid memory leaks this needs to be deleted in the class destructor
~A()
{
delete [] str;
}
Of course using std::string could save a lot of problems.
This answer will be used to contrast what the other answer(s) given that state to use std::string (and those answers are correct -- use std::string).
Let's assume that you could only use char *, you can't for some reason use std::string, and that you are dealing with NULL terminated strings. This is a synopsis of what your implementation would have to do (and please compare this with simply using std::string):
#include <algorithm>
#include <cstring>
class A
{
public:
// construct empty string
A () : str(new char[1]()) {}
// construct from non-empty
A(const char *s) : str(new char[strlen(s) + 1])
{ strcpy(str, s); }
// copy construct
A(const A& rhs) : str(new char[strlen(rhs.str) + 1])
{ strcpy(str, rhs.str); }
// destruct
~A() { delete [] str; }
// assign
A& operator=(const A& rhs)
{
A temp(rhs);
std::swap(str, temp.str);
return *this;
}
// setter
void setStr(char * s)
{
A temp(s);
*this = temp;
}
// getter
const char* getStr() { return str; }
private:
char * str;
};
Live Example
After adding a couple more constructors and a getter function, this follows the Rule of 3.
You see how much code we needed to add just to make the class safely copyable and assignable? That's why using std::string is much more convenient than using char * when it comes to class members. For std::string a single line needs to be changed, compared to adding the copy / assignment (and move, which I didn't show) functions.
The bottom line is that in C++ if you want strings, use strings (std::string) and try to keep away from using char * (unless you have a very compelling reason to be using char * to represent string data).
Here is my code.
std::shared_ptr<WSUStudent> WSUStudent::registerStudent(
std::string lastName,
std::string firstName
)
{
auto result = std::shared_ptr<WSUStudent>(new WSUStudent(lastName, firstName));
s_allStudents.insert(&result);
return result;
}
I have successfully managed to change the function so it returns a shared_ptr instead of a normal pointer. I have successfully encapsulated the 'new' statement with a shared pointer, as per the assignment (I think), but the line of code below 'auto' didn't work without the &, and it doesn't work WITH the &. I receive an error stating that there is no matching function call, with or without the &. That line of code is attempting to insert the new student (or a pointer to the new student?) into the list of all students. However the 'insert' method is not locally overridden, so I'm not quite sure what to do here. Error printed below.
/mnt/hgfs/Data Structures and Algorithms/HW04/WSUStudent.cpp:146:32: error: no matching function for call to ‘std::set<WSUStudent*>::insert(std::shared_ptr<WSUStudent>*)’
s_allStudents.insert(&result);
The point of this assignment is to fix memory leaks ('new' statements that don't get deleted with their pointers) by turning normal pointers into weak pointers and shared pointers. The original code is as follows.
WSUStudent *WSUStudent::registerStudent(
std::string lastName,
std::string firstName
)
{
auto result = new WSUStudent(lastName, firstName);
s_allStudents.insert(result);
return result;
}
Am I going about this wrong? I can't get the s_allStudents line to run.
Given the type of s_allStudents, you can use:
s_allStudents.insert(result.get());
However, a better option will be to change type of s_allStudents.
static std::set<std::shared_ptr<WSUStudent>> s_allStudents;
and use:
s_allStudents.insert(result);
Update
The default operator<() of shared_ptr is such that the objects in s_allStudents will be sorted by pointer value. If you would like to sort the objects using a different criterion, you'll need to define a custom functor/function as a parameter of the template.
struct MyCompare
{
bool operator<(shared_ptr<WSUStudent> const& lhs,
shared_ptr<WSUStudent> const& rhs) const
{
// Implement the logic ...
}
};
and use it as:
static std::set<std::shared_ptr<WSUStudent>, MyCompare> s_allStudents;
If you are going to return a std::shared_ptr<WSUStudent> then you are returning ownership rights to the object you created - meaning someone else will be trying to delete it at some point.
Unless you also keep ownership that means your pointer could get deleted before you are finished with it. So you need to also store std::shared_ptr in your static set:
I am guessing at how you are using this class but what I mean goes something like this:
class WSUStudent
{
// you really need to store shared pointers in here
static std::set<std::shared_ptr<WSUStudent>> s_allStudents;
std::string lastName;
std::string firstName;
// only the static factory function can make students
WSUStudent(
const std::string& lastName, // passing by const& is more usual (idiomatic)
const std::string& firstName)
: lastName(lastName)
, firstName(firstName)
{
}
public:
static std::shared_ptr<WSUStudent> registerStudent(
const std::string& lastName,
const std::string& firstName);
};
std::shared_ptr<WSUStudent> WSUStudent::registerStudent(
const std::string& lastName,
const std::string& firstName
)
{
auto result = std::shared_ptr<WSUStudent>(new WSUStudent(lastName, firstName));
// put the shared student in your set
s_allStudents.insert(result);
return result;
}
// define your set
std::set<std::shared_ptr<WSUStudent>> WSUStudent::s_allStudents;
int main ()
{
// make students
auto s = WSUStudent::registerStudent("bill", "bob");
// all deletions should be in order
}
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?
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....
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!
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.
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.
I'm new to and learning C++. I know a fair amount of Java and some C.
What I want to do is to create an immutable name class that takes in a string value, copies that string to a class field and then eventually hashes it to an ID that can be parsed much more efficiently than a string.
I'm hitting a wall due to a general lack of knowledge of C++ strings. Here's what I have so far...
#pragma once
#include <string>
class Name
{
public:
Name(std::string s);
~Name(void);
int getId();
std::string getName();
private:
int id;
std::string name;
};
and...
#include "Name.h"
Name::Name(std::string s)
{
}
So what I want to do is store the value of s, passed in by the constructor in the "name" private field. As far as I know a new string object must be created and then the value of s must be copied into it.
I also think that the argument s can and should be a string pointer instead of a string object (to prevent an unnecessary copy from occurring). If I'm right then the constructor should look like the following, right?
Name::Name(std::string &s) { ... }
In this case, nothing would need to be done special when passing in a name? IE.
Name n = new Name("Cody");
is perfectly valid? Actually I'm not sure since "Cody" to my knowledge is a constant string or something like that.
So if I'm all on the right track, then what is the proper way to actually copy the value? I'm thinking this is appropriate but I'm not sure.
#include "Name.h"
Name::Name(std::string s)
{
name = new string(s);
}
Thanks for the help in advance, I know it's a basic question but I'm slowly making baby steps into the C++ world. :) - Cody
You are close, your code can be like this after a little massage:
class Name
{
public:
Name(const std::string& s); // add const and reference
~Name(void);
int getId() cosnt; // add const
std::string getName() const; // add const
private:
int id;
std::string name;
};
Name.cpp
Name::Name(const std::string& s):name(s)
{
}
Here :name(s) is called member initializer list.
Name n = new Name("Cody"); is perfectly valid? Actually I'm not sure
since "Cody" to my knowledge is a constant string or something like
that.
No, n is not pointer, it's not like java you need to new for every object. In C++, you do
Name n("Cody");
This will call Name(const std::string& s) to initialize object n and initialize name string with "Cody".
Note: variable n has automatic storage duration, it will be destroyed if it goes out of scope.
To let n on dynamic storage duration, you need to use new/delete pair:
Name *pn = new Name("Cody");
delete pn;
or use smart pointers, you no need to call delete n_ptr; as n_ptr will be destroyed when it goes out of scope as well:
#include <memory>
std::shared_ptr<Name> n_ptr(new Name("Cody"));
EDIT:
To use Name class in other classes, it's the same way when you use string in Name class, you don't have to use pointers.
class TestName
{
public:
TestName(const Name& n):name_(n){ }
private:
Name name_;
};
TestName tn("Cody");
You should use a constant reference to std::string here.
As you said, it would prevent unnecessary copies.. But then why not just a pointer or a constant pointer?
A constant reference would allow you to pass to your function some arguments that would implicitly call the right std::string constructor.
So, in a nutshell, you could do that:
Name::Name(const std::string& s)
{
this->name = s;
}
// Or even better..
Name::Name(const std::string& s):
name(s)
{
}
int main(void)
{
Name nick("hello");
return 0;
}
You can find out about every std::string's constructors on its cplusplus.com's sheet.
I have a very simple class named person which is given below , I have a problem with only two functions , i.e setstring () function and setname() function , I am calling setstring() function from the setname function.
The only problem is when in the main function I write
Object.setname(“Zia”);
The result is ok as shown in the output screen,
Now when I write
Object.setname(“Zia ur Rahman”);
Nothing is displayed as you can see the output screen.
I know the problem is when I pass the name pointer to setstring () function but I am confused about it please explain it in detail that what is happening here.
#include<iostream.h>
class person
{
char* name;
public:
person();
void setname(const char*);
void setstring(const char*, char*);
void print()const;
};
person::person()
{
name=new char[3];
strcpy(name,"NILL");
name[3]='\0';
}
void person::setstring(const char* s, char*p)
{
if(s!=NULL)
{
delete[] p;
p=new char[strlen(s)];
strcpy(p,s);
p[strlen(s)]='\0';
}
}
void person::setname(const char* n)
{
setstring(n, name);//passing data member name
}
void person::print()const
{
cout<<"Name: "<<name<<endl;
}
main()
{
person object;
object.setname("Zia ur Rahman");
object.print();
system("pause");
}
alt text http://img264.imageshack.us/img264/8867/inheritanceimage004.jpg
alt text http://img263.imageshack.us/img263/3770/inheritanceimage003.jpg
The specific reason that nothing is being printed is that in setstring, p is copy of the name pointer, not a reference to it. Try changing the signature of setstring to:
void setstring(const char* s, char*& p);
(note the &).
See the other answers for other significant errors in the code - unless these problems are fixed, you are likely to get crashes or strange behaviour.
And unless the purpose of the code is just to learn dynamic arrays, use std::string instead :-).
If you want to do correct OO-oriented programming in C++ you should maybe stay away from direct pointer management but use the STL string class and use references instead of pointers. Then you should have an easier time and your source should produce the correct output.
Otherwise check your constructor of the person class, the name-array has just 3 elements but you're referencing the 4th one there (indices 0-2!). Also in the setstring() method you're not allocating enough space for the trailing '\0' in the array!
For starters:
name=new char[3];
strcpy(name,"NILL");
name[3]='\0';
name has three elements - you are treating it as if it had five. In a similar vein:
p=new char[strlen(s)];
should be:
p=new char[strlen(s) + 1];
In fact, that function is completely wrong - you are supposed to be copying the string into 'name', not the mysterious 'p' parameter. If you really want that function, then 'p' must be a pointer to a pointer or a reference to a pointer.
Have you thought about using std::string from STL?
Though first problem I see is this.
person::person()
{
name=new char[3];
strcpy(name,"NILL");
name[3]='\0';
}
Your allocating an array of char's, the size of the array is 3 characters, then your copying "NILL" into it with strcpy so your filling in the array with all the characters but without the null terminator \0. "NILL" is a const string and such has a null terminator that is implicit but not shown for example "NILL\0". The \0 is a control character that is used to indicate the end of the string. Then you have an index out of bounds when you access the 3rd element of name array,when your size of your array is 3.
To help you find out the other parts that could be going wrong, here are some of the links to the function you use.
strcpy
strlen
strcpy will copy the whole string from one buffer to the next including the null terminator control character.
Strlen will return amount of characters between the beginning of the string and the terminating null character. Though it will not count the null character as a character. Thats why you will see suggestions with strlen(string)+1 to include the null terminator.
Though your doing well keep up the work, you will understand these little gotcha when using the standard library functions.
Suppose s have 5 chars. Then new char[strlen(s)]; allocates the memory of 5 chars. Then p[strlen(s)]='\0' is equivalent to p[5]=0. It's very very bad. p[5] does not exsist.
void person::setstring(const char* s, char*p)
{
if(s!=NULL)
{
delete[] p;
p=new char[strlen(s)];
strcpy(p,s);
p[strlen(s)]='\0'; //<<<< LOOK! Out of range
}
}
It's rather C, than C++ code.
There is C++ equivalent:
#include <string>
#include <iostream>
class person
{
std::string name_;
public:
person();
//preferable design is to set object invariant in appropriate constructor
explicit person(const std::string &name);
std::string get_name() const;
void set_name(const std::string &name);
void print()const;
};
person::person()
: name_("NILL")
{}
person::person(const std::string &name)
: name_(name)
{}
std::string person::get_name() const
{
return name_;
}
void person::set_name(const std::string &name)
{
name_ = name;
}
void person::print()const
{
std::cout<<"Name: "<<name_<<std::endl;
}
int main()
{
person person1;
person1.set_name("Zia ur Rahman");
person1.print();
//this is better design decision
//because after constructor we have valid object
person person2("Zia ur Rahman");
person2.print();
std::cin.get();
return 0;
}