human header
class human
{
char name[];
public:
void setName(char nameValue[]);
char* getName();
}
human cpp
void human::setName(char nameValue[])
{
char name[] = "walter";
}
char* human::getName()
{
return name;
}
main cpp
int main()
{
human myHuman;
char name[] = "walter";
myHuman.setName(name);
char* result3 = myHuman.getName();
cout << "My human has a name of " << result3 << endl;
return 0;
}
I assign the string "walter" but when I print it I get "╠╠╠╠╦ß╩d└²╒".
I don't understand which part is wrong. Can you tell me what's wrong with my code?
First and foremost you are assigning nameValue to a variable local to the function setName, which means that the class variable name is still unitialized when you return it in getName, hence the strange output.
The char name[]; declaration is also incorrect, C++ does not allow for variable length arrays or arrays with unspecified bounds unless they are immediately initialized in which case the size will be deduced given the size of the assigned string.
On that note, warnings must have been issued by your compiler, if not, crank them up, or better yet, make it treat warnings as errors, you'll have more robust code.
For the purpose of your assignment you should just go ahead and use pointers, a small problem arises because C++ does not allow for assignment of string literals to char* variables, this is easily fixed if you use const char*, your compiler may allow the former but it is illegal as per C++ rules.
Applying the corrections above, your code should look more like this:
class human
{
const char* name;
public:
void setName(const char *nameValue);
const char* getName();
};
void human::setName(const char *nameValue)
{
name = nameValue;
}
const char* human::getName(){
return name;
}
int main()
{
human myHuman;
const char* name = "walter";
myHuman.setName(name);
const char* result3 = myHuman.getName();
cout << "My human has a name of " << result3 << endl;
return EXIT_SUCCESS;
}
Related
i have a custom class, let's call it "Student" and a main method. I'm instanciating the class, and just want to output the content of the class.
My programm crashes with a: Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
Actual Code
Student.h
#ifndef PROG2_STUDENT_H
#define PROG2_STUDENT_H
#include <iostream>
class Student
{
private:
char *name;
char *firstName;
unsigned matriculationNumber;
unsigned semester;
public:
Student(char *name, char *firstName, unsigned matriculationNumber, unsigned semester);
~Student();
friend std::ostream &operator<<(std::ostream &ostream, const Student &student);
private:
};
#endif
Student.cpp
#include <cstring>
#include "Student.h"
Student::Student(char *name, char *firstName, unsigned matriculationNumber, unsigned semester)
{
std::strcpy(this->name, name);
std::strcpy(this->firstName, firstName);
this->matriculationNumber = matriculationNumber;
this->semester = semester;
}
Student::~Student()
{
delete[] this->name;
delete[] this->firstName;
}
std::ostream &operator<<(std::ostream &stream, const Student &input)
{
stream << input.name << ", " << input.firstName << ": "
<< input.semester << " Semester, MA " << input.matriculationNumber;
return stream;
}
and my main
#include <iostream>
#include "StudentPackage/Collection/StudentCollection.h"
int main()
{
Student studentOne((char *)"Testerson", (char *)"Test", 12345, 2);
std::cout << studentOne << std::endl;
return 0;
}
What i have tried
I have tried several things, including memcpy. But with memcpy I'm not able to detect the size of the char array correctly.
When i change the Student Constructor to the following, i get problems with the delete/free in the destructor. I guess, this isn't the correct way anyway, but this is happening, because the scope of the input variables are destroyed before the class destructor is called, correct?
Student::Student(char *name, char *firstName, unsigned matriculationNumber, unsigned semester)
{
this->name = name;
this->firstName = firstName;
this->matriculationNumber = matriculationNumber;
this->semester = semester;
}
Question
How can i correctly copy the char array from constructor (name to this->name)?
How can i correctly copy the char array from constructor (firstName to this->firstName)?
std::strcpy does not allocate memory. So your program is copying input to a "garbage" address that is occurred in the memory area where your Student object is placed. And as result no surprise that you are getting segment violation as result. There are two solutions:
a "C-style" way - allocate memory manually (i.e. like auto n = std::strlen(name); this->name = new char[n + 1]; std::strcpy(this->name, name);), but then you need to delete it manually (i.e. delete name;) in the destructor. btw, n + 1 because you also need room for the null-terminator, strlen result does not include it.
much better and more "C++" way - use std::string (i.e. declate name member variable as std::string). Then you just can do an asignment: this->name = name;, and no need manual memory management - std::string will take care.
(Code style) Also recommend to use some prefix or postfix for member variables, i.e. like m_name(more "Microsoft" style), or name_ - more "Google" style, to avoid those unnecessary this->.
This question already has answers here:
Why does calling std::string.c_str() on a function that returns a string not work?
(3 answers)
Closed 2 years ago.
I am new to C++ programming (work with Java mostly), and this behavior of C++ classes, member strings and string conversions to const char* with c_str() is confusing me.
I have a header, a class and main function as follows:
sample.h
class Sample
{
private:
int id;
std::string text;
public:
Sample(int id);
void setId(int id);
int getId();
void setText(std::string txt);
std::string getText();
void loadText();
~Sample();
}
sample.cpp
Sample::Sample(int id)
{
this->id = id;
}
void Sample::setId(int id)
{
this->id = id;
}
int Sample::getId()
{
return this->id;
}
void Sample::setText(std::string txt)
{
this->text = txt;
}
std::string Sample::getText()
{
return this->text;
}
void Sample::loadText()
{
this->text = "Loaded";
}
Sample::~Sample()
{
std::cout << "Destructor is called." << std::endl;
}
main.cpp
void main()
{
int id = 1;
Sample* sample = new Sample(id);
// Case: 1 - If I do this, it does not work. Prints gibberish.
sample->loadText();
const char* text = sample->getText().c_str();
std::cout << text << std::endl;
// Case: 2 - Otherwise, this works.
sample->loadText();
std::cout << sample->getText().c_str() << std::endl;
// Case: 3 - Or, this works
sample->loadText();
std::string txtCpy = sample->getText();
const char* text = textCpy.c_str();
std::cout << text << std::endl;
}
All three cases are done one at a time.
Case 3 does satisfy my use case (which is, passing the string to a C library that expects a const char*. But, I can't figure out the difference between Case: 1 and Case: 3? If we are returning the string by value, how does copying it to an intermediate variable make it kosher for the run-time?
The result of c_str() is only valid while the string you called it on still exists. In case 3, txtCpy still exists at the point you are writing cout << text. But in Case 1, the string was the return value of sample->getText which is temporary and stop existing at the end of that line .
This issue always will exist if you take pointers or references to other objects. A naked pointer or reference has its own lifetime which may differ from the lifetime of the targeted object. This is unlike Java where object references all participate in the lifetime of the object.
As such, you always need to think about object lifetimes when using these features, and it's commonly recommended to instead use higher level features or other code styles that do not permit lifetime management errors.
You could consider adding a member function to Sample which gets a const char * pointing at the original string (although this is a wee violation of encapsulation, and still has a similar class of problem if you hold onto the pointer and then modify the underlying string). Better would be to just avoid working with the naked pointers entirely.
In this code snippet
const char* text = sample->getText().c_str();
std::cout << text << std::endl;
the variable text is assigned by a pointer (c_str()) of a temporary object returned from the member function getText. After this statement the temporary object will nit be alive, So the variable text has an invalid pointer,
The code snippet could be valid if the member function returned reference to the data member text like for example
const std::string & Sample::getText() const
{
return this->text;
}
Pay attention to that this declaration of main
void main()
is not a standard declaration.
The standard declaration of main without parameters is
int main()
I am new to C++ OOP concepts and I come from a Java OOP background. I have tried this->name=name but I get an error and then I tried to use a pointer, but, even though the code runs, it does not 'save' the name in the object's name.
#include <iostream>
using namespace std;
class Person{
private:
char name[];
int age;
public:
Person(char name[], int age){
char* pName=this->name;
pName=name;
this->age=age;
cout<<this->name;
}
getInformation(){
cout<<"name: "<<this->name<<" age: "<<this->age;
}
};
int main(){
Person foo("foo",5);
}
In C++, arrays usually need to be declared with a length - in this case, char name[] would be invalid.
Additionally, arrays declared int array[5] (with a size) cannot be directly assigned to. i.e. this is invalid:
class A
{
public:
int array[5];
void foo() {
int other_array[5] = {0, 0, 0, 0, 0};
this->array = other_array;
}
}
Instead, you need to assign each element one at a time (e.g. in a loop):
for(int i = 0; i < 5; ++i) this->array[i] = other_array[i];
This is because in C++ arrays have space for all the elements, unlike Java where char name[] is a pointer to an array. If you want to be able to directly change what array a variable refers to, you should use a pointer instead - a pointer can be dereferenced like an array:
class A
{
public:
int *array;
int foo(int *other_array) {
this->array = other_array;
}
}
Though if you do this, be careful of memory management.
Ignoring (for the moment) the fact that the code is invalid and won't compile; here:
char* pName=this->name;
You instantiate a local pointer and assign the unitialised pointer name to it. Then you immediately reassign it with a pointer to the parameter name:
pName=name;
But both pName and name are temporary and are pointers to strings, not the strings themselves. Moreover pName is not a reference to this->name. So on return from the Person() constructor, the object remains unchanged (and uninitialised). You may have intended:
char*& pName = this->name ;
pName = name ;
But that is simply a convoluted form of:
this->name = name ;
and is still ill advised because you are then assigning this->name with the address of the literal string "foo" from main() which is const. In any case you are giving Person a pointer to data that belongs to something else and is not "owned" by Person, and Person has no knowledge of the data's lifetime or validity. It is recipe for all kinds of bugs and undesired behaviour.
Null-terminated (or ASCIZ or C-style) strings are not a true data type; it is merely a char array convention. Moreover arrays even are not first-class data types in C++ and you cannot assign one array to another or pass an array by value as a function argument - instead assignment and argument passing are pointer operations. In Person(), name is a pointer to the first character in the string constant "foo" not an independent string containing "foo".
To correct this using C-style strings:
#include <iostream>
#include <cstring>
class Person
{
private:
char name[256]; // << Must have size here.
int age;
public:
Person( const char* name, int age) // << Must have const arg for "foo" to be a valid parameter.
{
std::strncpy( this->name, // << C strings must be explicitly copied, not "assigned".
name,
sizeof(this->age) - 1 ) ;
this->age = age;
}
void getInformation() const // << must have return type, and may be const.
{
std::cout << "name: " << this->name << " age: " << this->age << "\n" ;
}
} ;
However in most cases it would be preferable to use C++ std::string objects:
#include <iostream>
#include <string> // << Use C++ string library
// (i.e. string rather than cstring/string.h)
class Person
{
private:
std::string name; // << std::string memory management is intrinsic
int age;
public:
Person( const std::string name, int age)
{
this->name = name ; // << Assignment possible due to
// operator overloading
this->age = age;
}
void getInformation() const
{
std::cout << "name: " << this->name << " age: " << this->age << "\n" ;
}
} ;
As said above, std::string is to recommend for now.
Not only for this problem, std::string is very nice to work with in many cases. especially in the learningface.
Just take a look at the string class here from MS' site:
https://learn.microsoft.com/en-us/cpp/standard-library/basic-string-class?view=vs-2019
If you're using c++, it's way better to use std::string to deal with strings.
But answring to your question, when you assign a value to any variable, it overrides its previous value and gets a new one.
In your code:
char* pName=this->name;
pName=name;
The first line assigns a new value to your pName variable. and the second line, simply overrides your first line, and copies something new into your pName variable. so, that's not a correct approach for what you want to do.
If you want to have a char * variable and copy content of another char * into it, I suggest something like:
private:
char *name;
...
public:
Person(char name[], int age){
this->name = strdup(name);
...
}
And be careful to free memory of name in your destructor function.
I have this code that works as expected:
#define MAX_PARAM_NAME_LEN 32
const char* GetName()
{
return "Test text";
}
int main()
{
char name[MAX_PARAM_NAME_LEN];
strcpy(name, GetName());
cout << "result: " << name << endl;
}
If I'd like to store the result to a char * (because some functions within a Frameworks I'm using use only char * as input) without using the strcpy (for practicality and readability of code, and learning too), how could I do? Keeping in const, this works well:
const char* name;
name = GetName();
but I still have const.
Trying to just use char*:
char* name;
name = GetName();
I get invalid conversion from 'const char*' to 'char*'. What's the best habit for this kind of conversion?
The best habit for this kind of conversion is to use std::string throughout your code. Since the framework that you are using takes const char* as its input, you can always pass it the results of c_str() call on your std::string:
std::string GetName() {
return "Test text";
}
int main() {
std::string name = GetName();
int res = external_framework_function(name.c_str());
cout << "result: " << res << " for " << name << endl;
}
A distant second best is using const char* in your code:
const char* name = GetName();
Since the framework that you are using takes const char* you are good here as well.
If you need a non-const pointer, there is no way around copying the string. You can make a function that does it for you, but you would remain responsible for freeing the copies that you get from it:
char* copy(const char* orig) {
char *res = new char[strlen(orig)+1];
strcpy(res, orig);
return res;
}
...
char *name = copy(GetName());
...
delete[] name;
return "Test text"; returns a pointer to a read-only string literal.
If you're using a function that takes a char* as an input, and you have a const char* (such as a read-only string literal), then you ought to supply a deep copy of the string starting at that const char* to such functions.
Else you risk undefined behaviour at runtime if a function attempts to modify a read-only string.
What you currently have is adequate; assuming you can't work with std::string. (If you can work with std::string and all your framework functions take a const char* input, then I'd suggest your refactoring your code to use a std::string, and pass the output of the c_str() method on that string class to your framework functions.)
Finally, if some of your framework functions require a char* then you could always build yourself a small adapter class:
class Adapter
{
public:
Adapter(const& Adapter) = delete; /*don't try to copy me please*/
Adapter& operator=(const Adapter& ) = delete; /*don't try to copy me please*/
Adapter(const char* s) : m_s(::strdup(s))
{
}
~Adapter() /*free memory on destruction*/
{
::free(m_s); /*use free to release strdup memory*/
}
operator char*() /*implicit cast to char* */
{
return m_s;
}
private:
char* m_s;
};
Then for a function void foo(char* c), you can call foo(Adapter("Hello"/*or any const char* */)); and foo can do as it pleases with the char* that's embedded in the anonymous temporary! You could even enhance this class to take a constructor to a char* where in that case only a shallow copy of the pointer is taken (and the destructor doesn't delete the memory).
In C++, the typical way to "drop const" is by using const_cast<>:
char *name = const_cast<char*>(GetName());
This is, of course, frowned upon, ugly and potentially dangerous, since it's really possible that GetName() returns a pointer to something that shouldn't be changed, and then you go and give yourself permission to change it. It's a good way to get very hard to find bugs, in that case.
A workaround is to use a std::string as a temporary holding area; it will mean copying the string but that might be acceptable performance-wise:
std::string s(GetName());
char *name = s.c_str();
This will of course only work if name isn't kept around when s goes out of scope. If that is the case, then you're going to have some form of persistent string storage layer.
You could explicitly cast it. (char*) getName(). But you probably shouldn't. Because the const bit means something like "promise not to change it". So if i have a function void foo(const char* string) I am saiying: "give me a pointer to a string. I won't change it."
And if you declare a variable const char* string = "hello"; You are saying, this string should not be changed. And because you make this promise, the compiler knows, and can make your code more efficient. This is why:
const char* a = "hello";
const char* b = "hello";
(a==b); //is probably true
your compiler knows you won't change a or b so it makes them point to the same address, so it only has to store one "hello" string. If you now go about changing a, b gets also changed, and this is not what you wanted.
So long story short, if you are absolutely sure that the function your calling does not change the string, you can explicitly cast it. (or better, change the function to (const char*) if it's yours).
If your not sure, you will have to make a copy. (Like you are already doing with strcpy()).
I've tried to write a class for a student information(name,IDno,and degree), this information will be printed on the screen; but I don't know where is the errors?!
#include <iostream>
#include <conio.h>
using namespace std;
class Student {
private:
char name;
int idnumber;
char degree;
public:
Student(char,int,char);
void setName(char n){name = n;}
int getName(){return name;}
void setIdnumber(char id){idnumber = id;}
int getIdnumber(){return idnumber;}
void setDegree(char d){degree = d;}
int getDegree(){return degree;}
};
Student::Student(char n,int id,char d){
name = n;
idnumber = id;
degree = d;
}
int main(){
Student s1, s2;
s1.setName(Sara);
s1.setIdnumber(333);
s1.setDegree(A);
s2.setName(Jack);
s2.setIdnumber(222);
s2.setDegree(B);
cout << "name: " << s1.getName() << ",IDnumber: " << s1.getIdnumber() << ",Degree: " << s1.getDegree() << endl;
cout << "name: " << s2.getName() << ",IDnumber: " << s2.getIdnumber() << ",Degree: " << s2.getDegree() << endl;
getch();
return 0;
}
Apparently, you have the following issues:
Student s1, s2;
This will try to call the default constructor. However, you defined a constructor that takes 3 parameters, which inhibits the compiler to generate a default constructor for you, so you will fail to create those objects, which effectively makes your follow up member function calls fail.
s1.setName(Sara);
setName takes char type as parameter type, if you mean string literal "Sara", then you will have trouble. Similar issues can be found in other function calls. You should fix this.
Meanwhile, you should prefer to use member initialization list instead of using assignment in constructor body to initialize your members.
Student::Student(char n,int id,char d): name(n), idnumber(id), degree(d){}
make sure that your members are declared in order of name, idnumber, degree.
char is a single character, not a string. try using std::string instead.
also, to declare a string literal, surround your string in quotes ""
eg:
s1.setName("Sara");
also, to use std::string, you will need to #include <string>
There are few problems with your code
char name;
name here is just a char literal so it will only store one literal while you are trying to pass string Sara to it. So change name to array or pointer
char name[10] or char *name
Also while passing name pass it with double quotes like "Sara". Change all the formal arguments in the functions to char array or pointer.
Student s1, s2;
Here while creating objects s1 and s2 it will call default constructor, which you have not provided, So include default constructor in your code
Student(){}
s1.setDegree(A);
Here you are trying to pass a literal not a variable so quote it
s1.setDegree('A');
C++ provides string datatype which is much easy to use wrt to char or char[], so use it. You can operate upon it like you use other datatype like int, double.
That will avoid your hassle to specify array length, using strcpy() to copy strings.
string name;