Adding string value to map in c++ - c++

I am trying to add a char array value into a map, but on displaying the value of char array is not coming, however the integer value is displayed.
That is ii.first is not displayed, however ii.second is displayed correctly.
Here is the complete code which I am running,
#include <iostream>
#include <cstring>
#include <map>
#include <utility>
using namespace std;
class map_demo {
public:
class cmp_str {
public:
bool operator() (char const *a, char const *b) {
return std::strcmp(a, b) <0;
}
};
private:
typedef map <char*, int, cmp_str> ptype;
ptype p;
public:
void set_value() {
char name[20];
int empid;
cout<<"Enter the employee name\n";
cin.getline(name,20);
// cout<<"name entered=:"<<name;
cout<<"Enter the employee id\n";
cin>>empid;
this->p.insert(map<char *,int>::value_type(name,empid));
}
void get_value() {
cout << "Map size: " << p.size() << endl;
for(ptype::iterator ii=p.begin(); ii!=p.end(); ++ii) {
cout <<"the first="<< (*ii).first << ": " << (*ii).second << endl;
}
}
};
//=====================================================================
int main() {
map_demo mp1;
mp1.set_value();
mp1.get_value();
}
The output obtained on running the code:
Enter the employee name
farhan
Enter the employee id
909
Map size: 1
the first=: 909
Here the first = farhan:909, should be the correct output, can anyone make me understand where I am making it wrong??

The problem there as other mentioned is the char *. Also in you case the char * becomes dangling and you are actually pointing to garbage, the reason behind that is that when name goes out of scope that memory is freed, and you are still pointing to that memory, you actually need to copy the data in the map.
this one works
// ConsoleApplication1.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <map>
#include <utility>
using namespace std;
class map_demo
{
public:
class cmp_str
{
public:
bool operator() (char const *a, char const *b)
{
return std::strcmp(a, b) <0;
}
};
private:
typedef map <string, int> ptype;
ptype p;
public:
void set_value()
{
char name[20];
std::string inval;
int empid;
cout << "Enter the employee name\n";
cin.getline(name, 20);
inval = name;
//cout<<"name entered=:"<<name;
cout << "Enter the employee id\n";
cin >> empid;
//this->p.insert(map<char *, int>::value_type(name, empid));
this->p.insert(std::pair<string , int>(inval,empid));
}
void get_value()
{
cout << "Map size: " << p.size() << endl;
for (auto ii = p.begin(); ii != p.end(); ++ii)
{
std::string mysf(ii->first);
//std::cout << mysf << std::endl;
cout << "the first=" << mysf << ": " << (*ii).second << endl;
}
}
};
int main()
{
map_demo mp1;
mp1.set_value();
mp1.get_value();
}
Is just a quick fix, probably with a bit more effort can be made better. But just to give you an idea.
If you need to do it with char *, then you probably need to allocate memory yourself in bulk, each time you go and ask for a name you copy that in your data struct and retrieve a pointer to it. To properly handle that the way you make your data struct changes a lot in how clean your result will be, but the core point is, you need to manage your memory, copy in a place which will persist and not get lost, and store a pointer to that memory, not to a region of memory freed when you get out of set_value().

This line
this->p.insert(map<char *,int>::value_type(name,empid));
adds a char* pointer to the map, not the string itself. If the pointer
points to the stack (name[] is on the stack) then it will be the potentially
the same address in each iteration.
Either use a std::string
e.g.
typedef std::map<std::string, int> ptype;
...
p.insert(std::make_pair(name,empid))
or allocate dynamic memory manually and keep track of the string
char* nameStorage = new char[strlen(name)+1];
strcpy(nameStorage,name);
p.insert(std::make_pair(nameStorage,empid));

You defined the key of the map like char *
typedef map <char*, int, cmp_str> ptype;
^^^^^
So in member function set_value
void set_value() {
char name[20];
int empid;
//...
this->p.insert(map<char *,int>::value_type(name,empid));
}
the key is assigned with the address of the local valriable name (more precisely with the address of the first character of the array name) that will be destroyed after exiting the function.
After that the key will be invalied because it will point to a non-existent character array.
Moreover the key shall be copy-assignable but arrays do not have the copy assignment operator.
You could use standard class std::array<char, 20> as the key type. For example
typedef map <std::array<char, 20>, int, cmp_str> ptype;
In this case you have to change also cmp_str that it would accept objects of this type.
Another approach is to use standard class std::string instead of the array. Foir example
typedef map <std::string, int> ptype;

Related

How to iterate through map when pass a class to map

I am writing a simple code that. I've created two objects from class Employee that have two method. getData() and displayData(), and I've made a map of Employee type. It is taking input but to iterate through map by calling the displayData() that is in class.
Here is the code. Ive got the error that error: 'struct std::pair<const int, Employee>' has no member named 'displayData'
Any suggestions or am i doing it wrong.
#include <iostream>
#include <map>
using namespace std;
class Employee {
private:
int id;
string name;
public :
void getData(){
cin >> id >> name;
}
void displayData(){
cout << id <<" "<<name<<endl;
}
};
int main() {
map<int, Employee> m1;
map<int, Employee> :: iterator it;
Employee e1,e2;
//for (i)
e1.getData();
m1[1] = e1;
e2.getData();
m1[2] = e2;
for (it = m1.begin(); it!=m1.end(); ++it){
cout << (*it).first.displayData() << (*it).second.displayData() <<endl;
}
return 0;
}
Thanks for the help in advance
Here is the code. Ive got the error that error: 'struct
std::pair<const int, Employee>' has no member named 'displayData'
The compiler tells you exactly what is wrong, you may have a Misconception about std::maps, std::maps holds a key-value pair(More details About Maps) which is a std::pair which can be accessed via an iterator or the value accessed directly via map[key]. iterator::first holds the key which in your code is an int, while iterator::second holds the value which in your case right now is an object called Employee, hence (*it).first.displayData() error.
This line is also incorrect, and would produce compiler errors.
cout << (*it).first.displayData() << (*it).second.displayData() <<endl;
(*it).second.displayData() will return the return type of the function since this is a function call, in this case a type of void. You can simply call the function like this to achieve your desired result in every iteration.
std::cout << it->first << " ";
it->second.displayData();
Always trust compiler errors.
In C++ 17, you can use structured bindings for more readbility:
for (auto [eid, employee] : m1)
{
std::cout << eid << ": ";
employee.displayData();
}

verify that members of c++ objects have constant address during runtime with vector example

I am trying to convince myself that objects in C++ have constant address during their lifetime. Here is a minimal working example:
#include <iostream>
#include <type_traits>
#include <vector>
class Class1
{
public:
Class1(unsigned int * pt);
unsigned int * val_pt;
};
Class1::Class1(unsigned int * pt)
:
val_pt(pt)
{}
class Class2
{
public:
Class2(std::vector<unsigned int> vec_);
std::vector<unsigned int> vec_of_ints;
Class1 class1_instance;
};
Class2::Class2(std::vector<unsigned int> vec_)
:
vec_of_ints(vec_),
class1_instance(Class1(&vec_of_ints[0]))
{}
int main() {
std::vector<unsigned int> vec_test(10, 2);
Class2 instance_class2(vec_test);
Class1 instance_class1 = instance_class2.class1_instance;
//both addresses are equal
std::cout<<"Address stored in instance_class1: "<<instance_class1.val_pt<<" ,address of first vec_element of instance_class2: "<<&(instance_class2.vec_of_ints)[0]<<std::endl;
instance_class2.vec_of_ints.resize(20);
//different addresses now
std::cout<<"Address stored in instance_class1: "<<instance_class1.val_pt<<" ,address of first vec_element of instance_class2: "<<&(instance_class2.vec_of_ints)[0]<<std::endl;
return 0;
}
My Class2 stores a vector of ints and an instance of Class1. Class1 stores the address of the vector of the Class2 instance.
I'd like to get the address of that vector, i.e. the address where the vector is stored on the stack. If my understanding is correct, the resize() function doesn't change that address on the stack but only the content of that address, i.e. where the vector points to in heap.
My overall goal is to show that any modifications of the vector in Class2 are "visible" in the stored pointer of Class1. So if I dereference the pointer in Class1 I will get the same integer value as when accessing the vector itself in Class2. That is because the address of member variables are constant during runtime.
But I guess something is wrong in my code, probably in the constructor where I pass 'vec[0]'. I think this is not the actual address of the vector in the stack but some address on the heap. How do I get the correct address?
Any input is appreciated!
What you're doing here is ungodly, and needs to stop. Consider this particularly terrible pattern:
class DataOwner {
public:
inline std::vector<uint32_t>& getData() { return data; }
private:
// I am safely tucked away
std::vector<uint32_t> data;
};
class I_Want_To_Work_On_Data {
public:
I_Want_To_Work_On_Data(DataOwner* owner) : owner(owner) {}
void doThing() {
auto& direct_ref_to_data = owner->getData();
for(auto& item : direct_ref_to_data) {
// This is just as fast as your direct pointer :/
}
}
private:
DataOwner* owner;
};
Returning mutable access to the data is somewhat bad, but it's far safer than the approach you are taking (in a single threaded environment a least). Performance is no worse than what you are attempting, but it is a lot safer. So what are you optimising this for exactly? How is your approach an improvement over this boring pattern?
Now you could argue that providing mutable access to the std::vector isn't wanted (i.e. don't allow any old code to resize the array), but that can easily be solved without resorting to dirty hacks.
#include <vector>
#include <cstdint>
class DataOwner {
public:
inline std::vector<uint32_t>::iterator begin()
{ return data.begin(); }
inline std::vector<uint32_t>::iterator end()
{ return data.end(); }
inline std::vector<uint32_t>::const_iterator begin() const
{ return data.begin(); }
inline std::vector<uint32_t>::const_iterator end() const
{ return data.end(); }
private:
// I am safely tucked away
std::vector<uint32_t> data;
};
class ConstAccess {
public:
ConstAccess(const DataOwner& owner) : owner(owner) {}
void doThing() {
for(const auto& item : owner) {
}
}
private:
const DataOwner& owner;
};
class MutableAccess {
public:
MutableAccess(DataOwner& owner) : owner(owner) {}
void doThing() {
for(auto& item : owner) {
}
}
private:
DataOwner& owner;
};
The performance is the same as with your approach, however this approach as the following advantages:
It won't crash in debug builds on this line: class1_instance(Class1(&vec_of_ints[0])), when the vector is empty, and you attempt to dereference NULL to find the address.
It won't crash when you attempt to dereference unsigned int * val_pt; after you've accidentally resized the array.
It won't allow you to accidentally do: delete [] val_pt
I'm not sure what conclusions you extracted from the comments and responses above.
I just wanted to make sure these were not among them:
The address of a member variable is constant during runtime.
If, for example, you have a vector of Class2 instances, and you
resize that vector, the address of the vec_of_ints member variable
may change for any of those instances.
Having a Class2 instance in the stack or a pointer to a Class2 instance in the heap makes a difference.
The address of the vec_of_ints member variable shouldn't change if
you resize it, no matter the instance of Class2 is in the stack or
in the heap.
The example below tests both assertions (https://godbolt.org/z/3TYrnjro8):
#include <iomanip>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
struct HoldsIntVector
{
std::vector<int> v{};
};
int main()
{
HoldsIntVector stackInstance{};
auto heapInstance{std::make_unique<HoldsIntVector>()};
stackInstance.v.push_back(5);
heapInstance->v.push_back(5);
auto printStackAndHeapInstances = [&](const auto& text){
std::cout << std::showbase << std::hex;
std::cout << &stackInstance << "\t" << &heapInstance << "\t";
std::cout << &stackInstance.v << "\t" << &heapInstance->v << "\t";
std::cout << std::setw(8) << std::setfill('0') << stackInstance.v.data() << "\t\t";
std::cout << std::setw(8) << std::setfill('0') << heapInstance->v.data() << "\t\t";
std::cout << text;
std::cout << "\n";
};
std::cout << "stackInstance\theapInstance\tstackInstance.v\theapInstance.v\t&stackInstance.v[0]\t&heapInstance.v[0]\n";
printStackAndHeapInstances("after initializing stack and heap instances");
// After resizing both vectors in stack and heap instances
//
// Address of v doesn't change neither in stack nor in heap instances
// Address of v[0] changes in both stack and heap instances
stackInstance.v.resize(10);
heapInstance->v.resize(10);
printStackAndHeapInstances("after resizing both v's");
std::cout << "\n";
// Now what happens if we have a vector of HoldsIntVector and we resize it
//
// Address of v changes for the first HoldsInVector
std::vector<HoldsIntVector> hivs{10};
std::for_each(begin(hivs), end(hivs), [](auto& hiv){hiv.v.push_back(3);});
std::cout << "&hivs[0].v\n" << &hivs[0].v << "\t" << "after intializing hivs\n";
hivs.resize(20);
std::cout << &hivs[0].v << "\t" << "after resizing hivs\n";
}

Can i save the pointers of some objects in a vector and than take that pointers to call an inline function

Lets suppose i have a class Player which has some meber function. One of them gives back the pointer of the object using the this keyword.
Player* getPlayer() { return this;};
class Player
{
public:
Player(int id, string name, int score = 0) : pId(id), pName(name), pScore(score) { };
void rollDice();
int getId() { return pId; }
int getScore() { return pScore; }
string getName() { return pName; }
Player* getPlayer() { return this;};
private:
int pId;
std::string pName;
int pScore;
unsigned char playerDiceRolled;
Dice mDice;
};
I also have a class called Game which saves the information of any player in different vectors by using a function. One of this vectors saves the pointer of the object vector playerList;
class Game
{
public:
void Winer();
void addPlayer(Player A) ;
void updateScore();
vector<Player*> playerList;
vector<int> idList;
vector<string> nameList;
vector<size_t> scoreList;
};
void Game::addPlayer(Player A)
{
this->playerList.push_back(A.getPlayer());
this->idList.push_back(A.getId());
this->nameList.push_back(A.getName());
this->scoreList.push_back(A.getScore());
}
My question is can i take the pointer out of that vector and use it to call a function of that class and return the wright value.
i tried that but it doen't work
I can see that the other values of the Player are saved correctly but when i call a function using the pointer to get a value it returns null.
For example
int main()
{
Game Game1;
Player A(0, "Player1");
Player B(1, "Player2");
Game1.addPlayer(A);
Game1.addPlayer(B);
cout << "Name is:" << Game1.nameList[1] << "\n";
cout << "Name is:" << Game1.playerList[1]->getName() << "\n";
return 0;
}
The cout gives the wright name of player for Game1.nameList[1] but NULL value for
Game1.playerList[1]->getName()
That's because you are storing a pointer to a copy of your object:
void Game::addPlayer(Player A)
You need to use a reference and store it:
void Game::addPlayer(Player& A)
Otherwise you get an undefined behavior.
addPlayer(Player A) takes a copy of the Player object. And that copy of A is destructed at the end of the method addPlayer. With this->playerList.push_back(A.getPlayer()); you save the pointer to a temporary object in the vector.
You should think over your code design in general. The game has list of Players so the game has either a shared or an exclusive ownership of those Player objects. And because of that you should not use a raw pointer here.
If you have an exclusive ownership you would use:
std::vector<std::unique_ptr<Player>> playerList;
Or
std::vector<Player> playerList;
For a shared ownership you would use:
std::vector<std::shared_ptr<Player>> playerList;
So a setup could look like this:
int main()
{
Game Game1;
Game1.addPlayer(std::move(std::make_unique<Player>(0, "Player1"));
Game1.addPlayer(std::move(std::make_unique<Player>(0, "Player2"));
cout << "Name is:" << Game1.nameList[1] << "\n";
cout << "Name is:" << Game1.playerList[1]->getName() << "\n";
return 0;
}
class Game {
public:
void Winer();
void addPlayer(Player A);
void updateScore();
vector<std::unique_ptr<Player>> playerList;
vector<int> idList;
vector<string> nameList;
vector<size_t> scoreList;
};
void Game::addPlayer(std::unique_ptr<Player> A)
{
this->idList.push_back(A->getId());
this->nameList.push_back(A->getName());
this->scoreList.push_back(A->getScore());
// adding A to the vector has to be the last action, because A is moved to the vector
this->playerList.push_back(std::move(A));
}
Raw pointer are now bad in general, but you don't use them when you want to express ownership. So if you pass a Player object to a function that should not take ownership of that object, then you would pass it as raw pointer:
void print_player_info(Player * const p) {
// only prints the player info so no ownership is taken
// p is only use within print_player_info
cout << "Name is:" << p->getName() << "\n";
}
int main()
{
Game Game1;
Game1.addPlayer(std::move(std::make_unique<Player>(0, "Player1"));
Game1.addPlayer(std::move(std::make_unique<Player>(0, "Player2"));
print_player_info(Game1.playerList[1]->get());
return 0;
}

Prevent vector items from being moved

this is a learning question for me and hopefully others as well. My problem breaks down to having a pointer pointing to content of a vector. The issue occurs when I erase the first element of the vector. I'm not quite sure what I was expecting, I somehow assumed that, when removing items, the vector would not start moving objects in memory.
The question I have is: is there a way to keep the objects in place in memory? For example changing the underlying container of vector? With my particular example, I will remove the pointer access and just use and id for the object since the class needs a ID anyway.
here is a simplified example:
#include <iostream>
#include <vector>
class A
{
public:
A(unsigned int id) : id(id) {};
unsigned int id;
};
int main()
{
std::vector<A> aList;
aList.push_back(A(1));
aList.push_back(A(2));
A * ptr1 = &aList[0];
A * ptr2 = &aList[1];
aList.erase(aList.begin());
std::cout << "Pointer 1 points to \t" << ptr1 << " with content " << ptr1->id << std::endl;
std::cout << "Pointer 2 points to \t" << ptr2 << " with content " << ptr2->id << std::endl;
std::cout << "Element 1 is stored at \t" << &aList[0] << " with content " << aList[0].id << std::endl;
}
What I get is:
Pointer 1 points to 0xf69320 with content 2
Pointer 2 points to 0xf69324 with content 2
Element 1 is stored at 0xf69320 with content 2
While you can't achieve what you want exactly, there are two easy alternatives. The first is to use std::vector<std::unique_ptr<T>> instead of std::vector<T>. The actual instance of each object will not be moved when the vector resizes. This implies changing any use of &aList[i] to aList[i].get() and aList[i].id to aList[i]->id.
#include <iostream>
#include <memory>
#include <vector>
class A
{
public:
A(unsigned int id) : id(id) {};
unsigned int id;
};
int main()
{
std::vector<std::unique_ptr<A>> aList;
aList.push_back(std::make_unique<A>(1));
aList.push_back(std::make_unique<A>(2));
A * ptr1 = aList[0].get();
A * ptr2 = aList[1].get();
aList.erase(aList.begin());
// This output is undefined behavior, ptr1 points to a deleted object
//std::cout << "Pointer 1 points to \t" << ptr1 << " with content " << ptr1->id << std::endl;
std::cout << "Pointer 2 points to \t" << ptr2 << " with content " << ptr2->id << std::endl;
std::cout << "Element 1 is stored at \t" << aList[0].get() << " with content " << aList[0]->id << std::endl;
}
Note that ptr1 will point to a deleted object, as such it's still undefined behavior to deference it.
Another solution might be to use a different container that does not invalidate references and pointers. std::list never invalidates a node unless it's specifically erased. However, random access is not supported, so your example can't be directly modified to use std::list. You would have to iterate through the list to obtain your pointers.
Not sure if this is what you want, but how about this:
(only basic layout, you need to fill in the details, also: haven't tested the design, might have some flaws)
template <class T>
class MagicVector {
class MagicPointer {
friend class MagicVector;
private:
MagicVector* parent;
unsigned int position;
bool valid;
MagicPointer(MagicVector* par, const unsigned int pos); //yes, private!
public:
~MagicPointer();
T& content();
void handle_erase(const unsigned int erase_position);
}
friend class MagicPointer;
private:
vector<T> data;
vector<std::shared_ptr<MagicPointer> > associated_pointers;
public:
(all the methods you need from vector)
void erase(const unsigned int position);
std::shared_ptr<MagicPointer> create_pointer(const unsigned int position);
}
template <class T>
void MagicVector<T>::erase(const unsigned int position){
data.erase(position);
for(unsigned int i=0; i<associated_pointers.size(); i++){
associated_pointers[i].handle_erase(position);
}
}
template <class T>
std::shared_ptr<MagicPointer> MagicVector<T>::create_pointer(const unsigned int position){
associated_pointers.push_back(std::shared_ptr<MagicPointer>(new MagicPointer(this, position)));
return std::shared_ptr<MagicPointer>(associated_pointers.back());
}
template <class T>
MagicVector<T>::MagicPointer(MagicVector* par, const unsigned int pos){
parent = par;
position = pos;
if (position < parent->data.size()){
valid = true;
}else{
valid = false;
}
}
template <class T>
T& MagicVector<T>::MagicPointer::content(){
if(not valid){
(handle this somehow)
}
return parent->data[position];
}
template <class T>
void MagicVector<T>::MagicPointer::handle_erase(const unsigned int erase_position){
if (erase_position < position){
position--;
}
else if (erase_position == position){
valid = false;
}
}
template <class T>
MagicVector<T>::MagicPointer::~MagicPointer(){
for(unsigned int i=0; i<parent->associated_pointers.size(); i++){
if(parent->associated_pointers[i] == this){
parent->associated_pointers.erase(i);
i=parent->associated_pointers.size();
}
}
}
Basic idea: You have your own classes for vectors and pointers, with pointer storing a position in the vector. The vector knows it's pointers and handles them accordingly whenever something is erased.
I'm not completely satisfied myself, that shared_ptr over MagicPointer looks ugly, but not sure how to simplify this. Maybe we need to work with three classes, MagicVector, MagicPointerCore which stores the parent and position and MagicPointer : public shared_ptr < MagicPointerCore>, with MagicVector having vector < MagicPointerCore> associated_pointers.
Note that the destructor of MagicVector has to set all of it's associated pointers to invalid, since a MagicPointer can outlive the scope of it's parent.
I was expecting, I somehow assumed that, when removing items, the vector would not start moving objects in memory.
How so? What else did you expect?? A std::vector guarantees a contiguous series of it's contained elements in memory. So if something is removed, the other elements need to be replaced in that contiguous memory.

What is correct way to use string as an argument and parameter or return type in C++

In this Code a method name setname().
In this method I am using parameter (Char *aname), argument "Name_Student" (When calling).
But getting error: "deprecated conversion from string constant to 'char*' [-Wwrite-strings]"
#include <iostream>
#include <string>
using namespace std;
class student
{
int rollno;
char *name;
public:
void setname( char *aname);
void setrollno(int arollno);
char getname() {return *name;}
int getrollno() {return rollno;}
};
void student::setname(char *aname)
{
name = aname;
}
void student::setrollno(int arollno)
{
rollno = arollno;
}
int main()
{
student astudent;
astudent.setname("Name_Student"); //Getting Error Here
astudent.setrollno(10);
astudent.getname();
astudent.getrollno();
return (0);
}
I want to know why I am getting this error. Can Anyone explain different scenarios to understand this.
When I am using casting in argument at the time of calling
.setname((char*)"Name_Student"); //Working Fine
When I am storing this string in an array and passing that array like
char name_s[] = "Name_Student";
.setname(name_s); // working fine
Probably what you are looking for is answered here.
Also here is a reply to the comment about strings that I have posted below the question:
In both C/C++ you can choose whether to pass by reference or value. There are two ways to do that - the * (pointer to) operator and & (address of) operator. However working with pass by value (not by reference) is totally okay in your case:
#include <iostream>
#include <string>
using namespace std;
class student
{
int rollno;
string name;
public:
void setname(string name) { this->name = name; }
void setrollno(int rollno) { this->rollno = rollno; }
string getname() { return name; }
int getrollno() { return rollno; }
};
int main()
{
student astudent;
astudent.setname("Name_Student"); // here you pass by value
astudent.setrollno(10);
astudent.getname(); // name is set, so everything is okay :)
astudent.getrollno();
return (0);
}
If you really want to go into the whole pass by reference thing you can easily modify the setname() function like this (you'll also have to modify the name member with string* name and the getname() with return *name):
void setname(string& name) { this->name = name; }
In this case however a call like
astudent.setname("Name_Student");
is not possible and you have to first create the object and then pass it to the function:
string newName = "Name_Student";
astudent.setname(newName);
This is useful in case you want to change the name outside the class however it might be viewed as something against the principles of OOP since altering an object's content should be done only via some strictly defined by the class it represents routines and not just like that.
Here is a redo of your code with a small example to illustrate what I mean:
#include <iostream>
#include <string>
using namespace std;
class student
{
int rollno;
string* name; // very important! otherwise you will just store a COPY of the new name instead of a reference to the object that was passed to setname()
public:
// here we pass by reference a string object!
void setname(string& name) { this->name = &name; }
void setrollno(int rollno) { this->rollno = rollno; }
string getname() { return *name; }
int getrollno() { return rollno; }
};
int main()
{
student astudent;
string str = "Name_Student";
astudent.setname(str); // we pass by reference here!
cout << astudent.getname() << endl;
str += "LALALA"; // because we have passed the string object by reference to the astudent object we can change its contents outside the class without using the setter provided by the class
cout << astudent.getname() << endl;
return (0);
}
The output from the code above will be:
Name_Student
Name_StudentLALALA
Then again in your case I do not believe you need any passing by reference. And a bit of advice - try to use the features provided by C++ when programming in C++. Although there are of course situation when you might want to stick to the old char* way of doing things in your case it just poor coding style. std::string also provides the c_str() function, which returns a C-string (see here for more information).
You should insert 'const' before 'char*', however this question has already been answered here: How to get rid of `deprecated conversion from string constant to ‘char*’` warnings in GCC?
Unless you have a specific reason to use char* you should use std::string. I think the idea of using std::string* is a terrible idea because the lifetime of the name of the student is separate from the student object. Consider the following code:
student get_student()
{
// get the student from the database
string name = "some name";
int rollno = 1;
student s;
s.setname(name);
s.setrollno(rollno);
return s;
}
If you then try to use the student's name you will be referencing an object that no longer exists. You can try it with:
student s = get_student();
cout << s.getname() << endl;
I think the best option is to use string for the name. That way you don't have to worry about memory allocation and lifetime management. You should also have a constructor instead of just setters (unless you are going for POD).
class student {
int rollno;
string name;
public:
student(const string& name, int rollno): name(name), rollno(rollno) {}
void setname(const string& name) { this->name = name; }
void setrollno(int rollno) { this->rollno = rollno; }
const string& getname() { return name; }
int getrollno() { return rollno; }
};
If your setname takes the argument as a const string& then you can still use setname("Name") because this will create an rvalue, which can bind to const reference.
int main(int argc, char *argv[]) {
student astudent("Student's Name", 10);
cout << "Roll #: " << astudent.getrollno() << '\n';
cout << "Name: " << astudent.getname() << '\n';
astudent.setname("New name");
cout << "Name: " << astudent.getname() << '\n';
string n{"Yet Another Name"};
astudent.setname(n);
cout << "Name: " << astudent.getname() << '\n';
return (0);
}