Is this approach change the object of the name only? - c++

I want Swap the name (i.e. string in cName[]) of the two cats using the pointer approach.
However I want to Only swap the name, NOT the object.
Am I correct?
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;
class CAT
{
public:
CAT(char * firstname) { strncpy(cName, firstname, 79); }
~CAT() { ; }
char * getName() { return cName; }
void setName(char *nameinput) { strncpy(cName, nameinput, 79); }
private:
char cName[80];
};
void nameSwap(CAT *CatA, CAT *CatB)
{
char testing[] = "testing";
CAT temp =CAT(testing);
temp = *CatA;
*CatA = *CatB;
*CatB = temp;
}
int main()
{
char Taby[] = "Taby";
char Felix[] = "Felix";
CAT pA = CAT(Taby);
CAT pB = CAT(Felix);
cout << "The inital name pA is " << pA.getName() << " and pA is" << pB.getName() << endl;
nameSwap(&pA, &pB);
cout << "After approach" << endl;
cout << "The name pA is " << pA.getName() << " and " << pB.getName() << endl;
system("PAUSE");
return 0;
}

You are actually swapping the whole objects, not only the name of the CAT.
If you only want to swap the name, you need to access the cName member in a similar way as you are doing for the objects. You'd also need permission to access to the cName member in such a swap function, which a function outside won't have since cName is private. Make the swap function a member of your class:
class CAT
{
public:
CAT(const char* firstname) { strncpy(cName, firstname, 80); }
~CAT() {}
const char* getName() const { return cName; }
void setName(const char *nameinput) { strncpy(cName, nameinput, 80); }
void swapName(CAT& CatB)
{
char tmp[80];
strncpy(tmp, CatB.cName, 80);
strncpy(CatB.cName, cName, 80);
strncpy(cName, tmp, 80);
}
private:
char cName[80];
// other CAT attributes won't be affected by the name swap
};
And call it like this
pA.swapName(pB); // or pB.swapName(pA); - same result
But consider using std::string instead of char[]. You'll soon find C++ strings much easier to work with and also, when swapping those, only the pointers to the underlying memory is swapped, so it's more effective.
std::string one;
std::string two;
one.swap(two);
Edit: As per request, I added a version using pointers.
I made it in a haste and haven't debugged it so I've probably made a lot of mistakes. First, I made a new class called wong_string that will hold the name and any other attributes suiteable for strings.
#include <stdexcept>
#include <cstring>
class wong_string {
char* m_data;
static char* duplicate(const char* str) {
size_t len = std::strlen(str)+1;
char* rv = new char[len];
std::memcpy(rv, str, len);
return rv;
}
public:
// default constructor: wong_string howdy1;
wong_string() : m_data(nullptr) {}
// conversion constructor: wong_string howdy2("value2");
wong_string(const char* cstr) :
m_data(duplicate(cstr))
{}
// copy constructor: wong_string howdy3 = howdy2;
wong_string(const wong_string& rhs) : wong_string(rhs.m_data) {}
// move constructor: wong_string howdy4 = wong_string("value4");
wong_string(wong_string&& rhs) : m_data(rhs.m_data) {
rhs.m_data = nullptr;
}
// copy assignment operator: (wong_string howdy5;) howdy5 = howdy4;
wong_string& operator=(const wong_string& rhs) {
if(this!=&rhs) {
char* tmp = duplicate(rhs.m_data);
if(m_data) delete []m_data;
m_data = tmp;
}
return *this;
}
// copy assignment operator from c string
wong_string& operator=(const char* rhs) {
*this = wong_string(rhs);
return *this;
}
// move assignment operator: (wong_string howdy6;) howdy6 = wong_string("value6");
wong_string& operator=(wong_string&& rhs) {
if(this!=&rhs) {
m_data = rhs.m_data;
rhs.m_data = nullptr;
}
return *this;
}
// destructor, free memory allocated by duplicate(), if any
~wong_string() {
if(m_data) delete []m_data;
}
// comparisons
bool operator==(const wong_string& rhs) const {
return strcmp(m_data, rhs.m_data)==0;
}
bool operator!=(const wong_string& rhs) const {
return !(*this==rhs);
}
// conversion to a normal c string
operator char const* () const { return m_data; }
// output stream operator
friend std::ostream& operator<<(std::ostream&, const wong_string&);
// input stream operator - not implemented yet
};
with that in place, your CAT can be made into something like this:
class CAT
{
public:
CAT(const char* firstname, const char* nickname=nullptr) :
cName(firstname),
cNickName(nickname?nickname:firstname)
{}
~CAT() {}
const char* getName() const { return cName; }
void setName(const char *nameinput) { cName=nameinput; }
void swapName(CAT& CatB)
{
std::swap(cName, CatB.cName);
}
private:
wong_string cName; // Madame Florence Jenkins III
// other CAT attributes won't be affected by the name swap
wong_string cNickName; // Ms. Miao
};
So, there you have it. Pointers galore...

Related

Sorting a vector with pointer at object C++

If anyone can help I would be very grateful.
How do i sort this vector:
vector<Person*>person
by this criterium:
Surname
I have already tried it using set but it removes object if there is more than 2 objects with same Surname
there are lot of string variables, and I need to sort it by
Surname
and then if surnames are the same, then I need to sort them by
Name
and also it sorts by hexadecimal value of that pointer...
EDIT:
More code as you ask:
for (pChild = pRoot->FirstChildElement("Member"); pChild != NULL; pChild = pChild->NextSiblingElement())
{
string Surname = pChild->Attribute("surname");
string Name = pChild->Attribute("name");
string DateOfBirth = pChild->Attribute("dateofbirth");
person.push_back(new Person(Surname, Name, DateOfBirth));
}
Without you showing more of your code, it is hard to help you, but I would look at the documentation for std::sort() as you can create custom operators to sort your vector.
Here's a complete example
#include <vector>
#include <iostream>
#include <algorithm>
class Person
{
public:
std::string s1, s2, s3;
Person(std::string S1, std::string S2, std::string S3) : s1(S1), s2(S2), s3(S3) {}
};
struct less_than_key
{
inline bool operator() (const Person* const p1, const Person* const p2)
{
if (p1->s1 < p2->s1)
return true;
else if (p1->s1 == p2->s1 && p1->s2 < p2->s2)
return true;
return false;
}
};
int main()
{
std::vector<Person*> persons{ new Person("C", "D", "E"), new Person("C", "C", "D"),
new Person("B", "C", "D"), new Person("B", "C", "E")};
std::sort(persons.begin(), persons.end(), less_than_key());
for (auto person : persons)
{
std::cout << person->s1 << ' ' << person->s2 << std::endl;
}
return 0;
}
I had a bit of fun doing it with std::set. There are a couple of examples of comparators. One function and one "functor."
#include <iostream>
#include <set>
#include <string>
struct Person {
uint64_t id;
std::string name;
std::string family_name;
bool operator<(const Person &other) const {
if (family_name == other.family_name) {
if (name == other.name) {
return id < other.id;
} else {
return name < other.name;
}
} else {
return family_name < other.family_name;
}
}
};
std::ostream &operator<<(std::ostream &os, const Person &x) {
return os << '{' << x.id << ", " << x.name << ", " << x.family_name << '}';
}
bool person_ptr_less(const Person *a, const Person *b) { return *a < *b; }
class PersonPtrComparator {
public:
bool operator()(const Person *a, const Person *b) const { return *a < *b; }
};
int main() {
std::set<Person *, bool (*)(const Person *, const Person *)> people(
person_ptr_less);
people.emplace(new Person{1, "Joe", "Smith"});
people.emplace(new Person{2, "Joe", "Blow"});
people.emplace(new Person{3, "Joa", "Smith"});
people.emplace(new Person{4, "Joe", "Smith"});
std::set<Person *, PersonPtrComparator> people_2(people.begin(),
people.end());
for (const auto &x : people) {
std::cout << *x << '\n';
}
std::cout << "---\n";
for (const auto &x : people_2) {
std::cout << *x << '\n';
}
return 0;
}
You can use a comparator like this:
// Simple class
class Person {
public:
string name;
Person(string name) {
this->name = name;
}
};
// create a comparator like this with two objects as parameters.
bool comparator(Person* a, Person *b) {
return a->name > b->name;
}
int main() {
vector<Person* > v;
v.push_back(new Person("ajay"));
v.push_back(new Person("tanya"));
// pass the comparator created into sort function.
sort(v.begin(), v.end(),comparator);
// printing output to check
for(int i=0;i<v.size();i++) {
cout<<v[i]->name<<endl;
}
}

No output from using array in class

I get a blank output. I'm a newbie and have been struggling on this for some time.
I have gotten 0 errors by the compiler.
Also what can be improved on this?
How can I get the length of const char* as an int instead of size_t without having to use static_cast.
#include <iostream>
#include <cassert>
class String
{
private:
char* Str_Buffer{};
int Str_Size{};
public:
String(const char* string = " ")
: Str_Size{ static_cast<int>(strlen(string)) }
{
Str_Buffer = new char[Str_Size];
}
String& operator=(const String& string)
{
if (this == &string)
return *this;
delete[] Str_Buffer;
Str_Size = string.Str_Size;
if (string.Str_Buffer)
{
Str_Buffer = new char[Str_Size];
for (int index{ 0 }; index < Str_Size; ++index)
Str_Buffer[index] = string.Str_Buffer[index];
}
return *this;
}
char& operator[](const int index)
{
assert(index >= 0);
assert(index < Str_Size);
return Str_Buffer[index];
}
friend std::ostream& operator<<(std::ostream& out, const String& string)
{
out << string.Str_Buffer;
return out;
}
~String()
{
delete[] Str_Buffer;
}
};
int main()
{
String word("Hello world!");
std::cout << word;
return 0;
}
I get a blank output.
You don't fill your String::Str_Buffer with meaningful data in the constructor. You could use std::strcpy() from <cstring> to do that. std::strlen() is also declared in that header file. To use std::strcpy() the memory pointed to by String::Str_Buffer needs to be one char bigger than the string you want to copy there because strings in C and C++ are zero-terminated ('\0').
How can I get the length of const char* as an int instead of size_t without having to use static_cast.
Why would you want an int? Sizes of objects in C++ are measured with values of type std::size_t (defined in several headers but when in doubt include <cstddef>). std::size_t is guaranteed to be big enough to handle all object sizes. It is for example the return type of std::strlen() and the sizeof-operator.
Your assignment operator is not exception-safe:
String& operator=(const String& string)
{
// ...
delete[] Str_Buffer; // the old state is now gone
Str_Size = string.Str_Size;
if (string.Str_Buffer)
{
Str_Buffer = new char[Str_Size]; // when new[] throws, the object
// will be in an undefined state
// ...
Possible but not elegant solution:
String& operator=(const String& string)
{
char *temp = new[string.Str_Size];
// copy string.Str_Buffer to temp
delete[] Str_Buffer;
Str_Buffer = temp;
Str_Size string.Str_Size
return *this;
}
See Copy-and-Swap for an better solution.
Resource Management
Please familiarize yourself with The Rule of Five and the Copy-and-Swap Idiom.
A starting point for a class that manages a string could look like that:
#include <cassert> // assert()
#include <cstddef> // std::size_t
#include <cstring> // std::strlen(), std::strcpy()
#include <utility> // std::swap(), std::exchange()
#include <iostream>
class string_t
{
size_t length = 0;
char *data = nullptr;
public:
string_t() = default;
string_t(char const *str)
: length { str ? std::strlen(str) : 0 },
data { new char[length + 1]{} }
{
str && std::strcpy(data, str);
}
string_t(string_t const &other) // copy constructor
: length { other.length },
data { new char[length + 1]{} }
{
other.data && std::strcpy(data, other.data);
}
string_t(string_t &&other) // move constructor
: length { std::exchange(other.length, 0) }, // steal others resources and
data { std::exchange(other.data, nullptr) } // give other a state it's
{} // destructor can work with
string_t& operator=(string_t other) // assignment operator
{ // mind: other gets copied
std::swap(length, other.length); // steal other's resources
std::swap(data, other.data); // other's destructor will
} // take care of ours.
~string_t() { delete[] data; }
std::size_t get_length() const { return length; }
char& operator[](std::size_t index)
{
assert(index < length);
return data[index];
}
// stream-insertion operator:
friend std::ostream& operator<<(std::ostream &os, string_t const &str)
{
return os << (str.data ? str.data : "");
}
};
int main()
{
string_t foo{ "Hello!" }; // char const* constructor
std::cout << foo << '\n';
string_t bar{ foo }; // copy constructor
std::cout << bar << '\n';
string_t qux{ string_t{ "World!" } }; // move constructor (from a temporary)
std::cout << qux << '\n';
bar = qux; // assignment operator
std::cout << bar << '\n';
}
First of all, you need to include for strlen. You get a blank output because the constructor does not write the input string to Str_Buffer. You may use std::copy to copy the memory to the allocated buffer.
You have to use static cast, because strlen returns std::size_t. Just change the type of Str_Size to std::size_t to get rid of the static cast.
Also take a look at the rule of five. Defining a move and copy constuctor will improve performace of your code.
See a working version of your code below:
#include <iostream>
#include <cassert>
#include <cstring>
#include <algorithm>
class String
{
private:
char* Str_Buffer;
std::size_t Str_Size;
public:
String(const char* string = " ")
: Str_Size{ strlen(string) }
{
Str_Buffer = new char[Str_Size];
std::copy(string, string + Str_Size, Str_Buffer);
}
String(const String& other)
: Str_Size(other.Str_Size)
{
Str_Buffer = new char[Str_Size];
std::copy(other.Str_Buffer, other.Str_Buffer + Str_Size, Str_Buffer);
}
String(String && other)
{
*this = std::move(other);
}
String& operator=(const String& string)
{
if (this == &string)
return *this;
delete[] Str_Buffer;
Str_Size = string.Str_Size;
if (string.Str_Buffer)
{
Str_Buffer = new char[Str_Size];
for (std::size_t index = 0; index < Str_Size; ++index)
Str_Buffer[index] = string.Str_Buffer[index];
}
return *this;
}
char& operator[](const int index)
{
assert(index >= 0);
assert(index < Str_Size);
return Str_Buffer[index];
}
friend std::ostream& operator<<(std::ostream& out, const String& string)
{
out << string.Str_Buffer;
return out;
}
~String()
{
delete[] Str_Buffer;
}
};
int main()
{
String word("Hello world!");
std::cout << word;
return 0;
}

c++ memory leak in constructor

I need to create 2 classes, and second class must have pointer to first object as its member. My program works, but there are memory leaks, and I can't fix them. I know, that the problem is in constructor or destructor, but I don't know how to fix it. Here is my code:
main.cpp
#include "classes.h"
int main() {
COne firstObj;
CTwo secondObj;
firstObj.setN(10);
firstObj.setS("candies");
firstObj.print();
secondObj.setS("cookies");
secondObj.setP(&firstObj);
cout<<endl;
secondObj.print();
return 0;
}
classes.h
#ifndef CLASSES_H
#define CLASSES_H
#include <iostream>
#include <string>
using namespace std;
class COne {
private:
int n;
string sOne;
public:
COne(int n2create = 0, const string& sOne2create = "");
COne(const COne& obj);
~COne();
COne& operator=(const COne& obj);
void setN(int n2set);
void setS(const string &sOne2set);
int getN() const;
string getS() const;
void print() const;
};
class CTwo {
private:
string sTwo;
COne* p;
public:
CTwo(string sTwo2create = "", COne& p2create = *(new COne()));
CTwo(const CTwo& obj);
~CTwo();
CTwo& operator=(const CTwo& obj);
void setS(string sTwo2set);
void setP(COne* p2set);
string getS() const;
COne* getP() const;
void print() const;
};
#endif
classes.cpp
#include "classes.h"
COne::COne(int n2create, const string& sOne2create) :n(n2create), sOne(sOne2create) {}
COne::COne(const COne& obj) :n(obj.n), sOne(obj.sOne) {}
COne::~COne() {}
COne& COne::operator=(const COne& obj){
n = obj.n;
sOne = obj.sOne;
return *this;
}
void COne::setN(int n2set) {n = n2set;}
void COne::setS(const string& sOne2set) {sOne = sOne2set;}
int COne::getN() const {return n;}
string COne::getS() const {return sOne;}
void COne::print() const {
cout<<"COne object:"<<endl;
cout<<"n = "<<n<<endl;
cout<<"sOne = \""<<sOne<<"\""<<endl;
}
CTwo::CTwo(string sTwo2create, COne& p2create) :sTwo(sTwo2create), p(new COne(p2create)) {}
CTwo::CTwo(const CTwo& obj) :sTwo(obj.sTwo), p(new COne (*obj.p)){}
CTwo::~CTwo() {delete p;}
CTwo& CTwo::operator=(const CTwo& obj){
sTwo = obj.sTwo;
p = obj.p;
return *this;
}
void CTwo::setS(string sTwo2set) {sTwo = sTwo2set;}
void CTwo::setP(COne* p2set) {p = p2set;}
string CTwo::getS() const {return sTwo;}
COne* CTwo::getP() const {return p;}
void CTwo::print() const {
cout<<"CTwo object:"<<endl;
cout<<"sTwo = \""<<sTwo<<"\""<<endl;
cout<<"p: n = "<<p->getN()<<", sOne = \""<<p->getS()<<"\""<<endl;
}
For one, in:
COne& p2create = *(new COne())
You have a memory leak if the argument is default constructed. This is because p2create is a reference to a dynamically allocated object and you then copy it into another dynamically allocated object only to store the latter and never free the former.
Specifically in:
CTwo::CTwo(string sTwo2create, COne& p2create) :sTwo(sTwo2create), p(new COne(p2create)) {}
Your code contains other errors of course. To solve them all, just rewrite your classes to:
struct COne {
int n;
std::string s;
void print() const {
std::cout << "COne object:\n"
<< "n = " << n << '\n'
<< "s = \"" << s << "\"\n";
}
};
struct CTwo {
std::string sTwo;
COne p;
void print() const {
std::cout << "CTwo object:\n"
<< "sTwo = \"" << sTwo << "\"\n"
<< "p: n = "<< p.n << ", s = \"" << p.s <<"\"\n";
}
};
which are functionally equivalent, and then use them as:
int main() {
COne firstObj {10, "candies"};
firstObj.print();
std::cout << '\n';
CTwo secondObj {"cookies", firstObj};
secondObj.print();
return 0;
}
Live demo
void CTwo::setP(COne* p2set) {p = p2set;}
Saves the new pointer, overwriting the previous value of p without deleting any previously allocated memory assigned to p.

MyStringStack c++ can't call method of my class

I have been trying to implement Stack that holds objects of MyString class (it is exercise from one of the books). I managed to push those objects to container, but when I want to call function void print() I get an error:
error: passing 'const MyString' as 'this' argument of 'void MyString::print()' discards qualifiers [-fpermissive]
cp->print();
Here is the code:
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class MyString
{
string a;
public:
MyString(string aa) : a(aa)
{
}
void print()
{
cout << a << endl;
}
};
class StringStack {
static const int size = 100;
const MyString* stack[size];
int index;
public:
StringStack();
void push(const MyString* s); // does this have to be const?
const MyString* pop(); // does this have to be const?
};
StringStack::StringStack() : index(0) {
memset(stack, 0, size * sizeof(MyString*));
}
void StringStack::push(const MyString* s) {
if(index < size)
stack[index++] = s;
}
const MyString* StringStack::pop() {
if(index > 0) {
const MyString* rv = stack[--index];
stack[index] = 0;
return rv;
}
return 0;
}
MyString s0("pralines & cream");
MyString s1("fudge ripple");
MyString s2("jamocha almond fudge");
MyString s3("wild mountain blackberry");
MyString s4("raspberry sorbet");
MyString s5("lemon swirl");
MyString s6("rocky road");
MyString s7("deep chocolate fudge");
MyString iceCream[] = {s0,s1,s2,s3,s4,s5,s6,s7};
const int iCsz =
sizeof iceCream / sizeof *iceCream;
int main() {
StringStack ss;
for(int i = 0; i < iCsz; i++)
ss.push(&iceCream[i]);
const MyString* cp;
while((cp = ss.pop()) != 0)
{
cout << (long)cp << endl;
// cp->print(); // won't work
}
} ///:~
You can either:
make void MyString::print() const, which seems to be reasonable.
or :
make const MyString* pop(); to return non const pointers.
I dont think that StringStack should store pointers to const objects. If you plan to never modify MyString objects from your stack, then keep them internally as const. The choice depends on your requirements.

Segmentation fault: 11 and malloc errors in C++ code

Ok, so I know there are probably a lot of errors in this code. I'm pretty new to dynamic memory allocation, pointers, etc.
The header file, account.h, is given to us by our professor. We were told not to make any changes to the .h file.
The implementation file is written by me. The main function is included just for basic initial testing. We were given another file to actually test the implementation of the account class.
If I don't comment out the cout name line, I get a seg fault 11 error.
If I do, it'll print the account number, but throw this error:
Test(29976) malloc: * error for object 0x62c1aa18c9d8374: pointer being freed was not allocated* set a breakpoint in malloc_error_break to debug
Abort trap: 6
Any help at all would be greatly appreciated!
Here's the header file:
class account
{
public:
typedef char* string;
static const size_t MAX_NAME_SIZE = 15;
// CONSTRUCTOR
account (char* i_name, size_t i_acnum, size_t i_hsize);
account (const account& ac);
// DESTRUCTOR
~account ( );
// MODIFICATION MEMBER FUNCTIONS
void set_name(char* new_name);
void set_account_number(size_t new_acnum);
void set_balance(double new_balance);
void add_history(char* new_history);
// CONSTANT MEMBER FUNCTIONS
char* get_name ( ) const;
size_t get_account_number ( ) const;
double get_balance( ) const;
size_t get_max_history_size( ) const;
size_t get_current_history_size ( ) const;
string* get_history( ) const;
friend ostream& operator <<(ostream& outs, const account& target);
private:
char name[MAX_NAME_SIZE+1]; //name of the account holder
size_t ac_number; //account number
double balance; //current account balance
string *history; //Array to store history of transactions
size_t history_size; //Maximum size of transaction history
size_t history_count; //Current size of transaction history
};
Here is the implementation file:
// File: account.cxx
// Author: Mike Travis
// Last Modified: Mar 3, 2012
// Description: implementation of Account class as prescribed by the file account.h
#include <cstdlib>
#include <stdio.h>
#include <iostream>
#include "account.h"
using namespace std;
//Constructor
account::account(char* i_name, size_t i_acnum, size_t i_hsize){
string *d_history;
d_history = new string[i_hsize];
for(int i = 0; i<i_hsize; i++){
name[i] = i_name[i];
}
ac_number = i_acnum;
history_size = i_hsize;
history_count = 0;
}
account::account(const account& ac){
string *d_history;
d_history = new string[ac.history_size];
for( int i=0; i<ac.get_current_history_size(); i++){
strcpy(d_history[i], history[i]);
}
strcpy(name,ac.get_name());
ac_number = ac.get_account_number();
history_size = ac.get_max_history_size();
history_count = ac.get_current_history_size();
}
account::~account(){ delete [] history; }
void account::set_name(char* new_name){ strcpy(name, new_name); }
void account::set_account_number(size_t new_acnum){ ac_number = new_acnum; }
void account::set_balance(double new_balance){ balance = new_balance; }
void account::add_history(char* new_history){
strcpy(history[history_count], new_history);
history_count++;
}
char* account::get_name() const {
char* name_cpy;
strcpy(name_cpy, name);
return name_cpy;
}
size_t account::get_account_number() const{ return ac_number; }
double account::get_balance() const{ return balance; }
size_t account::get_max_history_size() const{ return history_size; }
size_t account::get_current_history_size() const{ return history_count; }
//string* account::get_history() const{ return *history; }
int main(){
account test1("mike travis", 12345, 20);
//cout<<"\nname: "<< test1.get_name();
cout<<"\n\nacnum: "<<test1.get_account_number()<<"\n\n";
return 0;
}
In the destructor of account, you delete the history array. However, in the constructor, you allocate (and leak) an array which is stored in the local variable d_history. You presumably wanted to assign that to the member variable history instead - since you haven't, if you get to the destructor it gives you an error saying that you're freeing history but have never allocated it.
There's a similar error in the copy constructor as well.
There are also other errors in your code as well, which I assume you'll find as you go - get_name(), for example, is not going to work. I suspect the header file is not helping here, but there's not much to be done if you're not supposed to change that.
I've written a little bit code for you and corrected the epic mistakes (even in the header file, sorry ;)). It is still extremely ugly and c-ish, but maybe you can learn something reading it:
#include <cstddef>
#include <ostream>
class account
{
// The whole class makes no sense, since it has no useful
// member function or anything like this.
// Furthermore, the class provides almost full access to all its member variables.
// At this point one could just make everything public.
// This is not even exception safe when the constructor throws.
// A good implementation would use history_entry and history classes,
// together with std::string and std::vector/std::deque
// And it would provide some sort of functionality. ;)
public:
account(const char* name, unsigned number, std::size_t history_max_size);
account(const account& other);
~account();
const char* name() const;
unsigned number() const;
double balance() const;
const char* const* history() const;
std::size_t history_size() const;
unsigned history_max_size() const;
void set_name(const char* new_name);
void set_number(unsigned new_number);
void set_balance(double new_balance);
void add_history(const char* new_history);
private:
char* name_;
unsigned number_;
double balance_;
char** history_;
std::size_t history_size_;
const std::size_t history_max_size_;
};
std::ostream& operator << (std::ostream& stream, const account& a);
#include <cassert>
#include <cstring>
account::account(const char* name, unsigned number, std::size_t history_max_size)
: name_(0)
, number_(number)
, balance_(0.0)
, history_(new char*[history_max_size])
, history_size_(0)
, history_max_size_(history_max_size)
{
assert(name != 0);
assert(history_max_size != 0);
set_name(name);
}
account::account(const account& other)
: name_(0)
, number_(other.number_)
, balance_(other.balance_)
, history_(new char*[other.history_max_size_])
, history_size_(other.history_size_)
, history_max_size_(other.history_max_size_)
{
set_name(other.name_);
for (std::size_t i = 0; i != other.history_size_; ++i)
{
history_[i] = new char[std::strlen(other.history_[i]) + 1];
strcpy(history_[i], other.history_[i]);
}
}
account::~account()
{
delete[] name_;
for (std::size_t i = 0; i != history_size_; ++i)
delete[] history_[i];
delete[] history_;
}
const char* account::name() const
{
return name_;
}
unsigned account::number() const
{
return number_;
}
double account::balance() const
{
return balance_;
}
const char* const* account::history() const
{
return history_;
}
std::size_t account::history_size() const
{
return history_size_;
}
unsigned account::history_max_size() const
{
return history_max_size_;
}
void account::set_name(const char* new_name)
{
if (name_)
delete[] name_;
name_ = new char[std::strlen(new_name) + 1];
std::strcpy(name_, new_name);
}
void account::set_number(unsigned new_number)
{
number_ = new_number;
}
void account::set_balance(double new_balance)
{
balance_ = new_balance;
}
void account::add_history(const char* new_history)
{
if (history_size_ == history_max_size_)
{
delete[] history_[0]; // delete oldest entry
for (std::size_t i = 0; i != history_size_ - 1; ++i)
history_[i] = history_[i + 1];
--history_size_;
}
history_[history_size_] = new char[strlen(new_history) + 1];
std::strcpy(history_[history_size_], new_history);
++history_size_;
}
std::ostream& operator << (std::ostream& stream, const account& a)
{
return stream << "account [name: " << a.name() << ", number: "
<< a.number() << ", balance: " << a.balance() << ']';
}
#include <iostream>
int main()
{
account a("Hello!", 500, 5);
a.set_balance(12.546);
for (int i = 50; i--; )
a.add_history("Yaaay..");
//account b = a;
std::cout << a << '\n';
}