Implementing a copy constructor for a linked list - c++

I am trying to implement a copy constructor for a linked list with c++. Would it be possible to first copy the elements of the linked list in to an array and then make the array[i] = list? (list is taken as a parameter.)
template <typename Type>
Single_list<Type>::Single_list( Single_list<Type> const &list ):
list_head( 0 ),
list_tail( 0 ),
node_count( 0 ) {
// enter your implementation here
for(int i = 0; i < node_count; i++){
*tmp_array = new array[node_count];
tmp_array[i] = list;
}
Sorry in advance, I am new to coding..
Okay here it is:
template <typename Type>
class Single_list {
private:
Single_node<Type> *list_head;
Single_node<Type> *list_tail;
int node_count;
public:
Single_list();
Single_list( Single_list const & );
~Single_list();
// Accessors
int size() const;
bool empty() const;
Type front() const;
Type back() const;
Single_node<Type> *head() const;
Single_node<Type> *tail() const;
int count( Type const & ) const;
// Mutators
void swap( Single_list & );
Single_list &operator = ( Single_list const & );
void push_front( Type const & );
void push_back( Type const & );
Type pop_front();
int erase( Type const & );
// Friends
template <typename T>
friend std::ostream &operator << (std::ostream &, Single_list<T> const&);
};
template <typename Type>
Single_list<Type>::Single_list():
list_head( 0 ),
list_tail( 0 ),
node_count( 0 ) {
// empty constructor
}
template <typename Type>
Single_list<Type>::Single_list( Single_list<Type> const &list ):
list_head( 0 ),
list_tail( 0 ),
node_count( 0 ) {
Single_List<Type> *tmp_ptr = 0;
for(tmp_ptr = head(); tmp_ptr !== 0; tmp_ptr->next()){
//my copy constructor so far..
tmp_ptr->retrieve() = list;
}
}
template <typename Type>
Single_list<Type>::~Single_list() {
Single_list<Type> *tmp_ptr = 0; //temp pointer initialized
for(tmp_ptr = head(); tmp_ptr !==0; tmp_ptr-next()){
//iterate through single_list, then delete
delete tmp_ptr;
}
}
Oh and the operator:
template <typename Type>
Single_list<Type> &Single_list<Type>::operator = (Single_list<Type> const &rhs) {
Single_list<Type> copy( rhs );
swap( copy );
return *this;

If you're doing this to develop your coding skillz, then you may want to study the code that implements std::list<>. Otherwise, you can just use it.
The move constructor and move assignment operator = are already implemented in C++0x and C++11 in the Standard Template Library (STL). You can make use of them (with a compatible compiler) simply by including the appropriate header (#include <list>) and using it.
Here's a simple example to demonstrate:
#include <list>
using namespace std;
int main()
{
list<int> my_list;
for (int i = 0; i < 1000000; ++i) // make a big list
my_list.push_back(i);
list<int> my_copied_list = my_list; // copy the list using the conventional assignment operator (slow)
list<int> my_moved_list = move(my_list); // move the list using the move assignment operator (fast)
}

Related

Getting the max element in a vector of objects from a custom class

Consider the following blocks of code:
template<typename Prefs> class SocialPrefNode{
public:
// Constructors & Destructor
SocialPrefNode( );
SocialPrefNode( char self, std::vector<SocialPrefNode*> pref );
SocialPrefNode( const SocialPrefNode& copy );
~SocialPrefNode( );
// Setters
void set_id( char self );
void set_pref( std::vector<SocialPrefNode*> prefs );
void set_pref( SocialPrefNode& prefs );
void set_worse( std::vector<SocialPrefNode*> wrs );
void set_worse( SocialPrefNode& wrs );
void set_indiff( std::vector<SocialPrefNode*> indiff );
void set_indiff( SocialPrefNode& indiff );
// Getters
char get_id( ){ return id; }
std::vector<SocialPrefNode*> get_preferences( ){ return preferences; }
std::vector<SocialPrefNode*> get_worse( ){ return worsethan; }
std::vector<SocialPrefNode*> get_indiff( ){ return indifference; }
// Operators
SocialPrefNode& operator=( const SocialPrefNode& copy );
private:
char id{ };
std::vector<SocialPrefNode*> preferences{ };
std::vector<SocialPrefNode*> worsethan{ };
std::vector<SocialPrefNode*> indifference{ };
};
,and
std::vector<SocialPrefNode<char>> graph
, where the latter is made by pointing its elements to each other.
How can I get the maximum element, in terms of the size of the preferences vector's size, from graph?
I.e., I want to select elements from graph accordingly to preferences' size, in descending order.
I have considered using std::max_element( ), but it does not seem to apply to the aforementioned code, since I am using a vector of objects from a custom class where there are no properties to directly inform the compiler what is to be considered a greater or smaller element in the vector.
Thanks for the help.
You can use std::max_element with a custom comparison function that compares the two objects however you'd like. See overload #3.
#include <vector>
#include <algorithm>
#include <cassert>
class Node
{
public:
Node(const std::vector<int>& prefs) : prefs_(prefs) {}
size_t numPrefs() const { return prefs_.size();}
bool operator==(const Node& rhs) const { return prefs_ == rhs.prefs_;}
private:
std::vector<int> prefs_;
};
int main(int argc, char** argv)
{
Node one(std::vector<int>{1});
Node two(std::vector<int>{1,2});
Node three(std::vector<int>{1,2,3});
std::vector<Node> nodes{one, two, three};
auto comparison = [](const Node& a, const Node& b)
{
return a.numPrefs() < b.numPrefs();
};
auto max = std::max_element(nodes.begin(), nodes.end(), comparison);
assert(*max == three);
//assert(*max == two); //Will fail
}
By default the standard library uses operator< to compare objects. So if your class SocialPrefNode<T> has either a function or member defined for operator< then it would work automatically.
//either:
template<typename T>
bool operator<(SocialPrefNode<T> const& lhs, SocialPrefNode<T> const& rhs);
// or
template<typename T>
class SocialPrefNode
{
public:
bool operator<(SocialPrefNod const& rhs) const;
};
If that is not viable then most standard library functions allow you to specify a comparator function as a last parameter. Looking at max_element this seems to be the case https://en.cppreference.com/w/cpp/algorithm/max_element
template< class ForwardIt, class Compare >
ForwardIt max_element( ForwardIt first, ForwardIt last, Compare comp );
The third parameter here is used to compare elements against each other. You can either pass a function, lamda, or object that can do the comparison.
auto m = std::max_element(std::begin(graph), std::end(graph),
[](auto const& lhs, auto const& rhs) -> bool
{
/* Do comparison */;
});

Use of class template requires template arguments

Hi I'm still wondering why I'm getting this error message :
Use of class template 'Array' requires template arguments
Header :
#ifndef Array_h
#define Array_h
template< typename T>
class Array
{
public:
Array(int = 5);
Array( const Array &);
const Array &operator=(Array &);
T &operator[](int);
T operator[](int) const;
// getters and setters
int getSize() const;
void setSize(int);
~Array();
private:
int size;
T *ptr;
bool checkRange(int);
};
#endif
CPP file
template< typename T >
const Array &Array< T >::operator=(Array &other)
{
if( &other != this)
{
if( other.getSize != this->size)
{
delete [] ptr;
size = other.getSize;
ptr = new T[size];
}
for ( int i = 0; i < size; i++)
{
ptr[i] = other[i];
}
}
return *this;
}
Problem seems to do with returning a const reference to object.
Thanks.
Before the compiler sees Array<T>::, it doesn't know that you are defining a member of the class template, and therefore you cannot use the injected-class-name Array as shorthand for Array<T>. You'll need to write const Array<T> &.
And you got constness backwards in your assignment operator. It should take a const reference and return a non-const one.
Also, Why can templates only be implemented in the header file?

Template copy constructor

I need to create a copy constructor for my class MyVector.
#include <iostream>
using namespace std;
template<class T>
class MyVector{
private:
T *v;
int size;
int max;
public:
MyVector();
MyVector(const MyVector &l);
MyVector& operator=(const MyVector &lhs);
T &operator[](unsigned int i);
};
int main() {
return 0;
}
template<class T>
MyVector& MyVector<T>::operator = (const MyVector &lhs){
if (this == &lhs) return *this;
for (int i = 0; i < size; ++i){
delete v[i];
}
delete [] v;
max = lhs.max;
size = lhs.size;
v = new T[max];
for(int i = 0; i < size; ++i) {
v[i] = new T(*(lhs.v[i]));
}
return *this;
}
im getting the error: expected constructor, destructor, or type conversion before âmyVectorâ
Not sure where the problem is, I am fairly new to c++.
Thanks.
The following is the problem:
template<class T>
MyVector& MyVector<T>::operator = (const MyVector &lhs){
/* ... */
}
The return type, MyVector& should be MyVector<T>& because you're outside of the class scope, so you need to provide the template parameter there, like this:
template<class T>
MyVector<T>& MyVector<T>::operator = (const MyVector &lhs){
/* ... */
}
In the return for the function implementation, write MyVector<T>.
template<class T>
MyVector<T>& MyVector<T>::operator = (const MyVector &lhs)
{
Note that you're implementing an assignment operator, not a copy constructor.
A nice alternative is to use C++11 syntax where the return type follows the function head:
template<class T>
auto MyVector<T>::operator = (const MyVector &lhs)
-> MyVector&
{
MyVector(const MyVector &l);
should be:
MyVector(const MyVector<T> &l);
and similarly everywhere else you use MyVector as a type, since the type is not complete without the template arguments.

avoiding dangling reference in array subscription operator

How to avoid dangling reference in array subscription operator in some vector implementation below? If realloc changes the pointer then references previously obtained from operator[] are no longer valid. I cannot use new/delete for this. I have to use malloc/realloc/free.
template <class T>
class Vector
{
public:
typedef size_t size_type_;
...
T& operator[] (const size_type_);
void push_back (const T&);
...
private:
const size_type_ page_size_;
size_type_ size_;
size_type_ capacity_;
T* buffer_;
};
template<class T>
inline
T&
some_namespace::Vector<T>::operator[] (const size_type_ index)
{
ASSERT(index < size_);
return buffer_[index];
}
template<class T>
inline
void
some_namespace::Vector<T>::push_back(const T& val)
{
if (size_ >= capacity_)
{
capacity_ += page_size_;
T* temp = (T*)(realloc(buffer_, capacity_*sizeof(T)));
if (temp != NULL)
{
buffer_ = temp;
}
else
{
free(buffer_);
throw some_namespace::UnexpectedEvent();
}
}
buffer_[size_++] = val;
}
By the way, the source of dangling reference in the code was this:
v_.push_back(v_[id]);
where v_ is an instance of Vector. To guard against this the new push_back is:
template<class T>
inline
void
some_namespace::Vector<T>::push_back(const T& val)
{
if (size_ >= capacity_)
{
const T val_temp = val; // copy val since it may come from the buffer_
capacity_ += page_size_;
T* temp = (T*)(realloc(buffer_, capacity_*sizeof(T)));
if (temp != NULL)
{
buffer_ = temp;
}
else
{
free(buffer_);
throw some_namespace::UnexpectedEvent();
}
buffer_[size_++] = val_temp;
}
else
{
buffer_[size_++] = val;
}
}
There are basically three things you can do:
Accept it as is and document it. This is what the STL does and it's actually quite reasonable.
Don't return a reference, return a copy. Of course, this means you can't assign to the vector's element anymore (v[42] = "Hello"). In that case you want a single operator[] which is marked const.
Return a proxy object which stores a reference to the vector itself and the index. You add an assignment to the proxy from const T& to allow writing to the vector's element and you need to provide some way to access/read the value, most likely an implicit operator const T&. Since the proxy object asks the vector on each access for the current position of the element, this works even if the vector changed the location of those elements between calls. Just be careful with multi-threading.
Here's a sketch for the proxy, untested and incomplete:
template<typename T> class Vector
{
private:
// ...
// internal access to the elements
T& ref( const size_type_ i );
const T& ref( const size_type_ i ) const;
class Proxy
{
private:
// store a reference to a vector and the index
Vector& v_;
size_type_ i_;
// only the vector (our friend) is allowed to create a Proxy
Proxy( Vector& v, size_type_ i ) : v_(v), i_(i) {}
friend class Vector;
public:
// the user that receives a Proxy can write/assign values to it...
Proxy& operator=( const T& v ) { v_.ref(i_) = v; return *this; }
// ...or the Proxy can convert itself in a value for reading
operator const T&() const { return v_.ref(i_); }
};
// the Proxy needs access to the above internal element accessors
friend class Proxy;
public:
// and now we return a Proxy for v[i]
Proxy operator[]( const size_type_ i )
{
return Proxy( *this, i );
}
};
Note that the above is incomplete and the whole technique has some drawbacks. The most significant problem is that the Proxy "leaks" in the API and therefore some use cases are not met. You need to adapt the technique to your environment and see if it fits.

Overloading operator[] for a template class- c++

just wrote a code for a template array class (I know its not finished yet), and trying to remember how to overload operators (meanwhile without special difficulties...).
Anyway, while thinking of how to implement operator[] I wondered what would happen if the index is outside the boundaries of the array... I'm pretty sure it is not possible for me to return a NULL (because of the return type), right? and if so, what should I return in case the index is out of boundries?
here's the code, most of it is redundant to my question, but it might help anyone who google's operators overloading so I post the complete code...
#ifndef __MYARRAY_H
#define __MYARRAY_H
#include <iostream>
using namespace std;
template <class T>
class MyArray
{
int phisicalSize, logicalSize;
char printDelimiter;
T* arr;
public:
MyArray(int size=10, char printDelimiter=' ');
MyArray(const MyArray& other);
~MyArray() {delete []arr;}
const MyArray& operator=(const MyArray& other);
const MyArray& operator+=(const T& newVal);
T& operator[](int index);
friend ostream& operator<<(ostream& os, const MyArray& ma)
{
for(int i=0; i<ma.logicalSize; i++)
os << ma.arr[i] << ma.printDelimiter;
return os;
}
};
template <class T>
T& MyArray<T>::operator[]( int index )
{
if (index < 0 || index > logicalSize)
{
//do what???
}
return arr[index];
}
template <class T>
const MyArray<T>& MyArray<T>::operator+=( const T& newVal )
{
if (logicalSize < phisicalSize)
{
arr[logicalSize] = newVal;
logicalSize++;
}
return *this;
}
template <class T>
const MyArray<T>& MyArray<T>::operator=( const MyArray<T>& other )
{
if (this != &other)
{
delete []arr;
phisicalSize = other.phisicalSize;
logicalSize = other.logicalSize;
printDelimiter = other.printDelimiter;
arr = new T[phisicalSize];
for(int i=0; i<logicalSize; i++)
arr[i] = other.arr[i];
}
return *this;
}
template <class T>
MyArray<T>::MyArray( const MyArray& other ) : arr(NULL)
{
*this = other;
}
template <class T>
MyArray<T>::MyArray( int size, char printDelimiter ) : phisicalSize(size), logicalSize(0), printDelimiter(printDelimiter)
{
arr = new T[phisicalSize];
}
#endif
operator[] generally does no bounds checking. Most of the standard containers that can have a range utilize a separate function, at(), which is range checked and throws a std::out_of_range exception.
You likely want to implement a const T& operator[] overload, too.