Constructors taking references in C++ - c++

I'm trying to create constructor taking reference to an object. After creating object using reference I need to prints field values of both objects. Then I must delete first object, and once again show values of fields of both objects. My class Person looks like this :
class Person {
char* name;
int age;
public:
Person(){
int size=0;
cout << "Give length of char*" << endl;
cin >> size;
name = new char[size];
age = 0;
}
~Person(){
cout << "Destroying resources" << endl;
delete[] name;
delete age;
}
void init(char* n, int a) {
name = n;
age = a;
}
};
Here's my implementation (with the use of function show() ). My professor said that if this task is written correctly it will return an error.
#include <iostream>
using namespace std;
class Person {
char* name;
int age;
public:
Person(){
int size=0;
cout << "Give length of char*" << endl;
cin >> size;
name = new char[size];
age = 0;
}
Person(const Person& p){
name = p.name;
age = p.age;
}
~Person(){
cout << "Destroying resources" << endl;
delete[] name;
delete age;
}
void init(char* n, int a) {
name = n;
age = a;
}
void show(char* n, int a){
cout << "Name: " << name << "," << "age: " << age << "," << endl;
}
};
int main(void) {
Person *p = new Person;
p->init("Mary", 25);
p->show();
Person &p = pRef;
pRef->name = "Tom";
pRef->age = 18;
Person *p2 = new Person(pRef);
p->show();
p2->show();
system("PAUSE");
return 0;
}

The problem with your copy constructor is that it merely assigns p.name:
name = p.name // Now this and p hold a pointer to the same memory
Since both this and p now hold a pointer to the same memory location, whichever one destructs first will free the memory while the second one will be holding a pointer to a non-existent object. Subsequently using that pointer or deleting it will result in undefined behavior. The solution is to allocate a new array for name and copy the contents of p.name into that array, so that the memory isn't shared.
Likewise, your init function overwrites name, ignoring the fact that memory has been allocating (it is a memory leak) and also ignoring the fact that the string will later be destructed (even though the caller probably expects to own and free that string, itself). Also, I should point out that your show function takes a parameter "n", but uses "name", instead. Your show function probably shouldn't take any parameters (indeed, the way you call it implies that it doesn't), given that all the needed fields are already present in your class (or maybe you intended for that to be a freestanding function that takes the fields of the class?). You should take another look at your code for additional errors.

First of all, try to compile your code (it is usually a good idea to compile code before posting it on SO.) It contains several errors and compiler will show them. Second part will be to change the following:
p->init("Mary", 25);
to
{
std::string mary("Mary");
p->init(mary.c_str(), 25);
}
It should give you an error at runtime and it will give you a chance to find a problem in your implementation.

Related

char* Input function Causes Seg Fault

Working on an assignment about Abstract Base classes, I'm running into a segment fault when I execute the getInput function I created in the addRecord function. I've tried a variety of methods for getting the user input for the name of the Employee/Student, however I keep running into issues with it.
getInput:
char* getInput(std::string message)
{
char* name;
std::cout << message << std::endl;
std::cin.ignore(INT_MAX, '\n');
std::cin.getline(name, INT_MAX);
return name;
}
addRecord:
/*
* addRecord(vecotr<base*>& v)
*
* Ask the user which type of record they want to add, an employee or student. Once
* they have made their selection, dynamically create a new pointer of the selected
* type of record and have them fill out the record's members via its set methods,
* then add it to the vector using the vector method .push_back().
*/
void addRecord(std::vector<Base*>& v)
{
std::cout << "--Records to Create--" << std::endl;
std::cout << "1.) Student Record" << std::endl;
std::cout << "2.) Employee Record" << std::endl;
std::cout << "Please select from the above options: ";
int sel = intInputLoop(1, 2);
clearConsole();
if (sel == 1)
{
char* name;
/*
std::cout << "Record Type: Student" << std::endl;
std::cin.ignore(INT_MAX, '\n');
std::cin.getline(name, INT_MAX);
*/
name = getInput("What is the Student's name? ");
float gradePointAverage = (float)intInputLoop(0, 4);
Student student = Student();
student.setName(name);
student.setGradePointAverage(gradePointAverage);
Base* bp = &student;
v.push_back(bp);
std::cout << "Added Student record for " << name << " with a grade point average of " << gradePointAverage << std::endl;
delete name;
}
else
{
char* name;
std::cout << "Record Type: Employee" << std::endl;
name = getInput("What is the Employee's name? ");
std::cout << "What is the Employee's salary? ";
int salary = intInputLoop(0, 0);
Employee employee = Employee();
employee.setName(name);
employee.setSalary(salary);
Base* bp = &employee;
v.push_back(bp);
std::cout << "Added Employee record for " << name << " with a salary of " << salary << std::endl;
delete name;
}
}
Any input is much appreciated. Thank you in advance.
char* name;
This declares a pointer, to an indeterminate number of char values. Who knows how many of them there are. It could be just one, very lonely char, sitting there, all by itself with nobody to play with. It could be a million, an entire city of chars. It's completely unspecified, nobody knows; that's because the pointer is completely uninitialized. Nothing in C++ happens automatically. If you intend to use a pointer, it must, well, point somewhere valid before you can use that pointer.
std::cin.getline(name, INT_MAX);
This call to getline reads input into a pointer. The pointer must point to valid memory. Since the pointer is uninitialized, this is undefined behavior, and is the reason for your crash.
Since your intent here is to use C++, the simplest solution is to use a C++ class that just happens to handle all the memory management for you: std::string:
std::string name;
std::getline(std::cin, name);
You'll need to replace all old-fashioned char * pointers, which is what you would use if you were writing C code, with std::string. After all: this is C++, not C.

Has no member named, BST

I'm creating a binary tree and trying to print the names of a student object which im passing in. When I try to print the tree, I get an error:
tree.h:181:46: error: ‘class samuel::Student’ has no member named ‘printInOrder’
str += Node->get_data().printInOrder() + "\n";
This is the function I'm calling in main using
BSTree<Student>* student_tree = new BSTree<Student>;
Student student = Student("Adam");
student_tree->insert(student);
student_tree->printInOrder();
string printInOrder(){return inOrder(root, 0);}
private:
string inOrder(BTNode<value_type>* Node, size_t level)
{
string str ="";
if(Node != NULL)
{
str += inOrder(Node->get_right(), level++);
for(int i = 1; i <= level; ++i)
{
str = str + "| ";
}
str += Node->get_data().printInOrder() + "\n";
str += inOrder(Node->get_left(), level++);
}
return str;
}
I'm not sure why when I try to access printInOrder it goes through Student. This is my student class
typedef Student value_type;
Student::Student()
{
}
Student::Student(std::string init_name, float init_grade)
{
name = init_name;
std::string studentName[50]={"Adam", "Cameron", "Jackson", "KiSoon", "Nicholas", "Adrian", "Chris", "Jacob", "Lance", "Ryan",
"Alexander", "Damian", "James", "Liam", "Sang", "Andrew", "David", "Jared", "Madison", "Shane", "Ashley", "Dillon",
"Jodi", "Magdalena", "Simon", "Benjamin", "Dylan", "Jonathan", "Marcus", "Thomas", "Bradley", "Ethan" "Joshua", "Mark",
"Timothy", "Brobie", "Frederik", "Julius", "Melanie", "Trent", "Callan", "Hong", "Kelly", "Min", "Troy", "Callum", "Hugh", "Kenias", "Mitchell", "Zaanif"};
for (int i = 0; i <50; i++)
{
int j = (rand() % (i-1));
string temp = studentName[j];
studentName[j] = studentName[i];
studentName[i] = temp;
}
}
Student::~Student()
{
}
void Student::set_name(string new_name)
{
name = new_name;
}
const string Student::get_name() const
{
return name;
}
void Student::set_grade(float new_grade)
{
grade = new_grade;
}
float Student::get_grade()
{
return grade;
}
An alternative method I tried was using
string infix(BTNode<value_type>* Node)
{
if (Node == NULL)
{
return "";
}else{
return (infix(Node->get_left()) + Node->get_data()) +
infix(Node->get_right());
}
}
friend ostream& operator << (ostream& out, const BSTree<value_type>& tree)
{
out << tree.infix(tree.root) << endl;
return out;
}
and then calling cout << student_tree << endl however this printed a memory address, would anyone also be able to clarify why that happens as well, thanks
Edit: Changed how I was inserting a student. Changed cout << student_tree << endlto cout << *student_tree << endlwhich gave the error
tree.h:70:9: error: passing ‘const samuel::BSTree’ as ‘this’ argument discards qualifiers [-fpermissive]
out << tree.infix(tree.root) << endl;
tree.h:181:46: error: ‘class samuel::Student’ has no member named ‘printInOrder’
Node->get_data() returns object of samuel::Student type because of this compiler searches for printInOrder() in samuel::Student type. It's not there according to the code above. To fix the issue implement the method:
std::string Student::printInOrder()
{
// Return the data to be printed
}
student_tree->insert(* new Student());
Looks suspicious. Tree contains Student objects by value. You create an Student object on the heap, dereference pointer and copies value into the Tree. Pointer lost after that. This will cause memory leak problem.
cout << student_tree << endl however this printed a memory address
Because it's declared as BSTree<Student>* student_tree. It's a pointer to the tree, so the output is correct, you print the address. To print the tree value you need to dereference the pointer: cout << *student_tree << endl.
continuation to other answer...
and then calling cout << student_tree << endl however this printed a
memory address, would anyone also be able to clarify why that happens
as well, thanks
BSTree<Student>* student_tree = new BSTree<Student>;
student_tree is a pointer to BSTree<Student> it means it holds the memory location(a memory address) of BSTree object which is an unnamed object in this case.
you must dereference it to get the value inside the address by doing *student_tree
std::cout << *student_tree; // actual value, and will call operator<<

Reference Parameters Address Automatically NOT passed

I have the following issue with reference parameters:
When we have a function with reference parameters, the compiler will automatically pass to that function, the address of any argument it is called with.
Example (with object):
class sample {
char *s;
public:
sample(); // normal constructor
sample(const sample &ob); // copy constructor
~sample( ) { if(s) delete [] s; cout << "Freeing s\n"; }
void show() { cout << s << "\n"; }
void set(char *str);
};// Definition will be excluded from here`
and we have a function with reference parameters of this class instance,
like:
void funtionWithRef(sample &kk); // declaration
void funtionWithRef(sample &sam){ // definition
sam.show();
}
and one function with return object of type sample:
sample functionReturnSample(); //declaration
sample functionReturnSample(){ // definition
sample sam;
sam.set("test sample");
return sam;
}
Now, when we do:
int main() {
sample temp = functionReturnSample();
funtionWithRef(temp);
return 0;
}
It works perfect. When we put temp object as an argument to funtionWithRef, compiler pass address of that object to the function.
But WHY it does not work, if we do NOT first assign a returning value of functionReturnSample to the instance, but directly put that method as an argument like :
funtionWithRef(functionReturnSample());
Why is this different, when we are doing the same thing, and according to some books I consulted, whit SHOULD
EDIT
#user657267
This is the full example (source book: C++ From Ground Up, 3rd Edition, page 219-320):
class sample {
char *s;
public:
sample(); // normal constructor
sample(const sample &ob); // copy constructor
~sample( ) { cout << "s: " << s <<" ,Freeing s\n"; if(s) delete [] s;}
void show() { cout << s << "\n"; }
void set(char *str);
sample operator=(sample &ob); // overload assignment
};
// Normal constructor.
sample::sample() {
s = new char('\0'); // s points to a null string.
cout << "Normal constructor: s: " << strlen(s) << endl;
}
// Copy constructor.
sample::sample(const sample &ob) {
cout << "Copy constructor: ob.s: "<< ob.s << " ,strlen(ob.s): " << strlen(ob.s) << "\n";
s = new char[strlen(ob.s)+1];
strcpy(s, ob.s);
}
// Load a string.
void sample::set(char *str) {
s = new char[strlen(str)+1];
strcpy(s, str);
}
// Overload assignment operator.
sample sample::operator=(sample &ob) {
/* If the target memory is not large enough
then allocate new memory. */
cout << "operator= strlen(ob.s): " << strlen(ob.s) << " ,strlen(s): " << strlen(s) << endl;
if(strlen(ob.s) > strlen(s)) {
cout << "operator= Larger memory of target object. Deleting current...\n";
delete [] s;
s = new char[strlen(ob.s)+1];
}
strcpy(s, ob.s);
return *this;
}
// Return an object of type sample.
sample input() {
char instr[80];
static sample str;
cout << "Enter a string: ";
cin >> instr;
str.set(instr);
return str;
}
int main() {
sample ob;
// assign returned object to ob
ob=input(); // This is now OK
ob.show();
return 0;
}
This wont compile, and reports error:
**error: no match for ‘operator=’ (operand types are ‘sample’ and ‘sample’)**
So it is copy/past of the code from the mentioned book. You can check please.
However I figureout if I specify overloaded = operator arguments to be const like:
sample operator=(const sample &ob); // overload assignment
Then it DOES work.
However, what is bothering me is, now when I have runnable code, I do not get why TWO times copy constructor is called.
I know it is called when input() function returns, and create temporary object, but I do not get why second time since, as much as I know (but maybe I am wrong) copy constructors are NOT called for assignment operations (same Book, pages 291-292), but it looks like, despite of that, when return *this; is called (when overloaded operator returns value), copy constructor is called?
So what is about that ?
Thankx

Access violation help C++

I am currently getting this error in my code: Unhandled exception at 0x0FF321E8 (msvcp110d.dll) in bankAccountp5.exe: 0xC0000005: Access violation writing location 0xCCCCCCF8. And I'm certain it's to do with an array of objects I have created.
Here is part of my code:
class bankAccount: public bankBranch
{
public:
bankAccount(int, int, int, string, int);
int setAccountNumber ()
{
bankAccountNumber = ++minAccountNumber;
return this->bankAccountNumber;
}
void setAccountBalance ()
{
for(i = 0; i < getNumOfBankAccounts(); i++)
{
cout << "Enter the balance for your bank Account: " << endl;
cin >> accounts[i]->bankAccountBalance;
if (bankAccountBalance > MAX_BALANCE || bankAccountBalance < MIN_BALANCE)
{
cout << "Incorrect bank balance, please try again!" << endl;
--i;
} else {
++i;
}
}
}
void menuSystem(int choice) {
}
void displayBankBranchDetails()
{
cout << "\n";
cout << "DETAILS OF YOUR BANK BRANCH" << endl;
cout << "BSB Number: " << this->getBsbNumber() << endl;
cout << "Address: " << this->getAddress() << endl;
cout << "Postal code: " << this->getPostCode() << endl;
}
void setBankAccountDetails() {
}
int getNumOfBankAccounts() {
return this->numOfBankAccounts;
}
void createBankAccounts()
{
valid = false;
while (valid == false) {
cout << "How many Bank Accounts would you like to create under the Bank Branch BSB: " << getBsbNumber() << endl;
cin >> numOfBankAccounts;
if ( numOfBankAccounts <= MAX_NUMBER_ACCOUNTS)
{
valid = true;
} else {
valid = false;
}
}
}
private:
//bankAccount Data
int bankAccountNumber;
int numOfBankAccounts;
int bankAccountBalance;
int interestRate;
//constants
const static int MAX_NUMBER_ACCOUNTS = 10;
const static int MAX_BALANCE = 100000;
const static int MIN_BALANCE = 0;
//objects
bankBranch homeBranch;
bankAccount* accounts[MAX_NUM_ACCOUNTS];
//misc
int minAccountNumber;
int i;
bool valid;
};
The error occurs when I get to void setAccountBalance(), and I call the array of objects accounts, could anyone help me out with this? Cheers.
You have declared an array of pointers, you have to assign memory to it dynamically, bankAccount* accounts[MAX_NUM_ACCOUNTS];
moreover you don't need to write the else part in setAccountBalance()
This
bankAccount* accounts[MAX_NUM_ACCOUNTS];
creates an array of pointers. You need to take the next step of actually allocating memory for each account. Something like
accounts[some-index] = new bankAccount();
accounts = new bankAccount[MAX_NUM_ACCOUNTS]; needs to be done in one of your functions. You have declared a dynamic array, but still need to initialize it.
EDIT: You should really consider using stl structure vector. This will allow you to just push new accounts into your array and a variety of other things. Using pointers and dynamically allocated arrays need you to manage your memory and other such unnecessary pains.
Check this out: http://www.cplusplus.com/reference/vector/vector/
Add using std::vector after your inclusions. This way you don't need to keep typing std::vector, you can just say vector.
Then, vector<bankAccount> accounts will declare a vector called accounts. When adding new accounts, you can just call accounts.push_back(_______). You can access elements with [] and you also have accounts.size().
As #scottwilson said. Instead use a std::vector<bankAccount> that contain statically allocated bankAccounts.
Else, you might have to allocate memory for each bankAccount pointer, either statically or as you might require, dynamically.
You also need a createBankAccount() function that will allocate this for you whenever you want another bankAccount object:
like so:
public:
void createBankAccount() {
accounts[getNumberOfBankAccounts()] = new bankAccount();
numOfBankAccounts++;
}
Call this function each time you need a new bank account.

Char* not retaining text inside my class - initialization constructor problem?

I have the following class (I've trimmed irrelevant stuff):
class Example
{
private:
char* name;
int value[4];
int numVals;
public:
Example();
Example(char name[], int numVals, int, int, int, int);
~Example();
};
And here is the initialization constructor:
Example::Example(char na[], int vals, int v1, int v2, int v3, int v4)
{
name = new char[strlen(na)+1];
strcpy(name, na);
numVals = vals;
value[0] = v1;
value[1] = v2;
value[2] = v3;
value[3] = v4;
// cout << name; // this DOES print out the correct text
}
In my main(), I have an array of Example class, Example myArray[numRecs]. I then have a loop that uses the initialization constructor to fill the array:
myArray[i] = Example(name, numVals, v[0], v[1], v[2], v[3]);
Everything works as expected, however the name does not retain the characters I put into it. I checked using cout what the value is when it is passed into the constructor, and it was correct! However when I use my Example::Print(), it spits out a random character (the same character for each instance of Example).
Here is the Example::Print()
void Example::Print()
{
int total, avg;
total = avg = 0;
cout << left << setw(20) << name << '\t';
for(int i=0; i<4; i++){
if(i<numVals){
cout << left << setw(8) << value[i];
total += value[i];
} else
cout << left << setw(8) << " ";
}
avg = total/numVals;
cout << left << setw(8) << total <<
left << setw(8) << avg << endl;
}
Any ideas? Thanks!
Oh and also, this is for an assignment. We have been told to keep the name field as a char pointer, not a string. I am confused as to what I should be using for the init constructor (char* name or char name[] or.. is there a difference?)
EDIT: Here is the destructor and default constructor. I do not have a copy constructor yet as the assignment does not call for it and it is not used. I'll go ahead and make one for completeness anyway.
Example::~Example()
{
delete [] name;
}
Example::Example()
{
numVals = 0;
for(int i=0; i<4; i++)
value[i] = -1;
}
You should run your program through a memory debugger to witness the nightmare you've created!
You are using manual memory management in your class, yet you forgot to obey the Rule of Three: You didn't implement the copy constructor, assignment operator and destructor! Thus the temporary does allocate memory, copies the pointer (shallowly), and then probably invalidates the memory when it goes out of scope.
The immediate "fix my code" answer is that you have to implement a proper assignment operator and copy constructor to make a deep copy of the char array.
The "this is C++" answer is not to use pointers, new and arrays, and instead std::string.
An easy fix would be to store pointers to the Examples in the array instead of the actual objects. This way, you don't have to deal with copying (which you haven't implemented).