The DStack example
==================
#ifndef DSTACK_H
#define DSTACK_H
#include <iosfwd>
class DStack
{
friend void print(std::ostream&, const DStack&);
public:
DStack();
DStack(const DStack& rhs);
DStack& operator=(const DStack& rhs);
~DStack();
int size() const;
bool empty() const;
double top() const;
double& top();
void push(double d);
void pop();
private:
int _size;
int _capacity;
double* _ptr;
void copy(const DStack& rhs);
void release();
void grow();
};
void print(std::ostream& os, const DStack& ds);
#endif
Friends:
========
Normally , private data members and memberfunctions are accessible
only by memberfunctions. Friend declarations make friend functions
access to private part.
Friend class declaration gives access right to all memberfunctions
of that class.
Use friends with care as an exception.
#include <stdexcept>
#include <iostream>
#include "dstack.h"
DStack::DStack()
{
_capacity = 4;
_size = 0;
_ptr = new double[_capacity];
}
DStack::DStack(const DStack& rhs)
{
copy(rhs);
}
DStack& DStack::operator=(const DStack& rhs)
{
if ( this != &rhs )
{
release();
copy(rhs);
}
return *this;
}
DStack::~DStack()
{
release();
}
void DStack::copy(const DStack& rhs)
{
_capacity = rhs._capacity;
_size = rhs._size;
_ptr = new double[_capacity];
for (int i = 0; i < _size; ++i)
_ptr[i] = rhs._ptr[i];
}
void DStack::release()
{
delete [] _ptr;
}
void DStack::grow()
{
double *_oldptr = _ptr;
_capacity = 2 * _capacity;
_ptr = new double[_capacity];
for ( int i = 0; i < _size; ++i)
_ptr[i] = _oldptr[i];
delete [] _oldptr;
}
int DStack::size() const
{
return _size;
}
bool DStack::empty() const
{
return 0 == _size;
}
void DStack::push(double d)
{
if ( _size == _capacity )
grow();
_ptr[_size] = d;
++_size;
}
void DStack::pop()
{
if ( 0 == _size )
throw std::out_of_range("stack empty");
--_size;
}
double DStack::top() const
{
if ( 0 == _size )
throw std::out_of_range("stack empty");
return _ptr[_size-1];
}
double& DStack::top()
{
if ( 0 == _size )
throw std::out_of_range("stack empty");
return _ptr[_size-1];
}
void print(std::ostream& os, const DStack& ds)
{
os << "[ ";
for (int i = 0; i < ds._size; ++i)
os << ds._ptr[i] << " ";
os << "]" << std::endl;
}
And here is the test program:
#include <iostream>
#include "dstack.h"
int main()
{
DStack x;
for (int i = 0; i < 10; ++i )
x.push(i);
DStack y;
for (int i = 0; i < 15; ++i )
y.push(i+20);
print( std::cout, x);
print( std::cout, y);
x = y;
std::cout << "x = y" << std::endl;
print( std::cout, x);
print( std::cout, y);
x.top() = 999;
std::cout << "x.top() = 999" << std::endl;
print( std::cout, x);
print( std::cout, y);
std::cout << "stress test for memory leaks..." << std::endl;
for(int i = 0; i < 1000; ++i)
{
std::cout << i << " ";
for(int j = 0; j < 1000; ++j)
{
DStack z1, z2;
z1.push(j); z2.push(j);
z1 = z2;
}
}
std::cout << " ... ok" << std::endl;
return 0;
}
$ g++ -ansi -pedantic -Wall -W dstack.cpp dstackmain.cpp
$ ./a.out
[ 0 1 2 3 4 5 6 7 8 9 ]
[ 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ]
x = y
[ 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ]
[ 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ]
x.top() = 999
[ 20 21 22 23 24 25 26 27 28 29 30 31 32 33 999 ]
[ 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ]
stress test for memory leaks...
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1
...
988 989 990 991 992 993 994 995 996 997 998 999 ... ok
But we can do it better, reusing DVector:
#ifndef DSTACK_H
#define DSTACK_H
#include <iosfwd>
#include "dvector.h"
class DStack
{
friend void print(std::ostream&, const DStack&);
public:
int size() const;
bool empty() const;
double top() const;
double& top();
void push(double d);
void pop();
private:
DVector _dvec;
};
void print(std::ostream& os, const DStack& ds);
#endif
We do not define special memberfunctions for DStack:
- default constructor
- copy constructor
- assignment operator=
- destructor
will be automatically created, and call memberwise operations on data,
which is the only DVector member.
Since we created the special memberfunctions for DVector, they will be used.
#include <stdexcept>
#include <iostream>
#include "dstack.h"
int DStack::size() const
{
return _dvec.size();
}
bool DStack::empty() const
{
return 0 == _dvec.size();
}
void DStack::push(double d)
{
_dvec.push_back(d);
}
void DStack::pop()
{
if ( 0 == _dvec.size() )
throw std::out_of_range("stack empty");
_dvec.pop_back();
}
double DStack::top() const
{
if ( 0 == _dvec.size() )
throw std::out_of_range("stack empty");
return _dvec[_dvec.size()-1];
}
double& DStack::top()
{
if ( 0 == _dvec.size() )
throw std::out_of_range("stack empty");
return _dvec[_dvec.size()-1];
}
void print(std::ostream& os, const DStack& ds)
{
os << "[ ";
for (int i = 0; i < ds._dvec.size(); ++i)
os << ds._dvec[i] << " ";
os << "]" << std::endl;
}
No change in the test program.
We have to link dvector to the program also.
$ g++ -ansi -pedantic -Wall -W dstack.cpp dstackmain.cpp dvector.cpp