1

I am practicing using templates and classes in C++. My goal is to write a template class for a deque. It will have functions to "insert_head", "insert_tail", "remove_tail", and "remove head", along with the ability to be printed using "cout". Also, the '=' operator must be able to be used to copy one instance of the class to another instance. Here is my current code:

#ifndef DEQUE_H
#define DEQUE_H

template <typename T>
class Deque {
public:
    Deque(int size = 0, int capacity = 1000) : size_(size), capacity_(capacity) 
    {}
    Deque(Deque & d) : x_(d.x()), size_(d.size()), capacity_(d.capacity()) {}


std::ostream & operator<<(std::ostream & cout) {
    cout << '[';

    if (size_ > 0) {
        for (int i = 0; i < (size_ - 1)* sizeof(T); i += sizeof(T)) {
            std::cout << *(x_ + i) << ',';
        }
        cout << *(x_ + (size_ - 1)* sizeof(T));
    }
    cout << ']';
    return cout;
}

Deque operator=(Deque d) {
    Deque dq(d);
    return dq;
}

void print_test() {
    std::cout << '[';

    if (size_ > 0) {
        for (int i = 0; i < (size_ - 1)* sizeof(T); i += sizeof(T)) {
            std::cout << *(x_ + i) << ',';
        }
        std::cout << *(x_ + (size_ - 1)* sizeof(T));
    }
    std::cout << ']';
}

int * x() {
    return x_;
}
int size() {
    return size_;
}
int capacity() {
    return capacity_;
}

bool is_empty() {
    return size_ == 0;
}

void insert_tail(T tail) {
    if (size_ < capacity_) {
        *(x_ + sizeof(T) * size_) = tail;
        size_++;
    } else {
        // throw overflow
    }
}
T remove_tail() {
    if (size_ > 0) {
        T ret = *(x_ + sizeof(T) * (size_ - 1));
        std::cout << ret;
        size_--;
        return ret;
    } else {
        // throw underflow
    }
}

void insert_head(T head) {
    if (size_ > 0 && size_ < capacity_) {
        for (int i = (size_ - 1) * sizeof(T); i < 0; i -= sizeof(T)) {
            *(x_ + i + sizeof(T)) = *(x_ + i);
        }
    }
    if (size_ < capacity_) {
        *x_ = head;
        size_++;
    } else {
        // throw overflow
    }
}

T remove_head() {

    if (size_ > 0) {
        T ret = *x_;
        for (int i = sizeof(T); i < size_* sizeof(T); i += sizeof(T)) {
            *(x_ + i - sizeof(T)) = *(x_ + i);
        }   
        size_--;
        return ret;
    } else {
        // throw underflow
    }
}

private:
    T * x_;
    int size_;
    int capacity_;
};

#endif

Here is my test code using that class:

#include <iostream>
#include "Deque.h"

int main(int argc, char const *argv[])
{
    Deque< int > dq;


    dq.insert_head(1);

    // dq.insert_head(2); // adding head when not empty causes bug

    dq.insert_tail(3);
    dq.insert_tail(4);
    dq.insert_tail(5);
    dq.print_test(); std::cout << std::endl;

    // std::cout << dq; // '<<' not overloaded properly'

    std::cout << dq.remove_head() << " head removed\n";

    // int x = dq.remove_head(); // seg faults when assigning returned value to a variable 

    dq.insert_tail(2);
    dq.print_test();
    std::cout << std::endl;

    Deque< int > dq1(dq);
    Deque< int > dq2;

    // dq2 = dq1; // '=' not overloaded properly

    return 0;
}

Each of my four problems is in a commented out line of code in my test file, here is a further explaination:

  1. When "dq.insert_head(2)" is called and dq is not empty (size > 0) I try to shift all the other elements in the deque over one position so I can insert the new value there, there is a problem and the elements are not moved over.

  2. "std::cout << dq" does not print dq like it should. The code is very similar to the "print_test()" method, however when I run the program I get the error "no match for operator <<". Is this because it is template class? Or am I doing something else completely wrong?

  3. When trying to remove the head or tail from the deque, I am trying to return the value removed. In the line of code not commented out, the returned value is printed as it should, but the following line of code causes a seg fault. Is it because I'm trying to assign a template varabale to an integer variable?

  4. My last issue is the '=' operator is not copying one instance of the class to another. My goal was to create a new instance of the class then return that instance (as you can see in the "Deque operator=(Deque d)") but that is not working as I hoped. What is the best way to overload the '=' function using template classes.

Thank you for your help, the answer to any of these questions is much appreciated.

Schnagl
  • 31
  • 4
  • 2
    where are you allocating memory for `x`? remove out your `sizeof(T)` terms. also, consider using const references where appropriate. – MFisherKDX Dec 12 '17 at 00:14
  • 1
    1) You can find the reason for 1, 3, 4 by stepping through your code with a debugger (that's what anyone who wanted to answer you, would, most likely, do anyway). 2) Regarding (2) - Your current overload accepts `deque` as a first argument, and `ostream` as a second, effectively allowing you to write ` dq << cout`. If you want to output it via the use of `cout << dq` overload it as a `friend`, having the proper signature (i.e. taking `ostream` as a first argument). – Algirdas Preidžius Dec 12 '17 at 00:17
  • 1
    Why did you overload the `=` operator? – Jake Freeman Dec 12 '17 at 00:17
  • 1
    Your `x` method unconditionally returns `int*`, not `T*`. Your `operator=` isn't assigning a thing to any members of the instance, it's just making a new `Deque` and returning it, which is pointless. It's hard/annoying to implement copy construction and `operator=`; I'd strongly recommending reading about and using [the copy-and-swap idiom](https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom) to reduce duplicate code dramatically; if copy construction and destruction works, `operator=` is easy. – ShadowRanger Dec 12 '17 at 00:20
  • 2
    and the `*(x+i)` syntax will likely end up confusing you. i'd suggest using array syntax `x[i]` – MFisherKDX Dec 12 '17 at 00:20
  • 1
    ... and to extend what @ShadowRanger points out, consider what happens to the copied Deque when you finally delete the original Deque and its associated `x` array. – MFisherKDX Dec 12 '17 at 00:22
  • 1
    Given `T * x_;`, `for (int i = sizeof(T); i < size_* sizeof(T); i += sizeof(T)) *(x_ + i - sizeof(T)) = *(x_ + i);` etc is wrong; use `for (int i = 1; i < size_; i++)`, and furthermore `size_t` would be better than `int`. – Ken Y-N Dec 12 '17 at 00:29
  • You could always look at STL code to have an idea how to implement a function. In any case, before attempting to write a class like `Deque` you should learn a bit more of the language. As written, all of your functions have important flaws in them. – Phil1970 Dec 12 '17 at 01:26

2 Answers2

2

All of your functions have issues:

 Deque(int size = 0, int capacity = 1000) : size_(size), capacity_(capacity) {}
  • If you allows to specify a size, then you would have to allocate and initialize memory for those items. You should only specify capacity.
  • x_ is not initialized.

Assuming, you want a fixed capacity, then your constructor should be:

Deque(int capacity = 1000) 
    : size_(0)
    , x_(new T[capacity])
    , capacity_(capacity) 
{
}

And even that is a simplified version as it would call the constructor for all items which might be inefficient and require that T has an accessible default constructor.

And now for the copy constructor:

  • The copy constructor should do deep copy. Otherwise, your program will (probably) crash after deleting the first Deque for which you have done copies as deleting an item twice is undefined behavior.
  • The prototype should take a constant reference as in: Deque(const Deque &other);

The code would look similar to this:

Deque(const Deque &other)
    : capacity_(other.capacity_)
    , x_(new T[other.capacity_])
    , size_(other.size_)
{
    for (int i = 0; i != size_; ++i)
    {
        x_[i] = other.x_[i];
    }
}

For the <<, the prototype should be:

friend std::ostream & operator<<(std::ostream &cout, const T &data)

assuming it is declared inside the class to access private fields. You need to pass the data on which the operator works.

For the assignment operator, something like this could works:

Deque& operator=(const Deque &other)
{
    // Use swap idiom...
    Deque tmp(other);

    // Swap pointers so old x_ get destroyed...
    T *old_x = x_;
    x_ = tmp.x_;
    tmp.x_ = old_x;

    // Usually one would use std::swap. 
    // Here as tmp get destroyed, it is not strictly to swap capacity_ and size_.
    capacity_ = tmp.capacity_;
    size_ = tmp.size_;
}

Now for the x() function: - If you do a queue, you probably don't want to expose data so the function should be removed. - If it was kept, the function should be const and returns a pointer to T: T *x() const; for the expected functionality.

size, capacity and is_empty should all be const member functions.

insert_tail and remove_tail problems have been explain in other people comments (in particular extraneous sizeof).

Similar problems for insert_head and remove_head also. In addition, the code that copy existing items could be refactored inside a private function to follows the DRY principle and avoid code duplication.

Phil1970
  • 2,605
  • 2
  • 14
  • 15
  • What would the function header be for the function outside the class that goes with the "friend std::ostream & operator<<(std::ostream &cout, T &data)" function declaration? – Schnagl Dec 12 '17 at 02:30
  • You would just remove the `friend` keyword. – Phil1970 Dec 12 '17 at 02:33
1

The answer to your first problem is to remove the sizeof(T) so you end up with this

    for (int i = (size_ - 1); i > 0; i --) {
        *(x_ + i + 1) = *(x_ + i);
    }

The answer to your second problem is to change your declaration for your << overload to friend std::ostream & operator<<(std::ostream & x, Deque n) and initialize the body outside the class.

The answer to the third problem is that you can't return an int pointer which may point to a different block of memory location that what T could be.

The answer to the fourth question is to do the following:

Deque& operator=(const Deque& d) {
    x_ = d.x_; // Deep copy
    size_ = d.size_;
    capacity_ = d.capacity_;
    return *this;
}
Jake Freeman
  • 1,700
  • 1
  • 8
  • 15
  • 1
    So in the class I put the prototype: friend std::ostream & operator<<(std::ostream & x, Deque n) What does the function header outside the class look like? I keep getting errors saying "Deque is not a type" – Schnagl Dec 12 '17 at 01:13
  • @Schnagl That is correct! If this works please do not forget to up vote and accept – Jake Freeman Dec 12 '17 at 01:14
  • That solution fixes some problems but not all. You `=` operator is still incorrect. To start, the prototype of assignment operator should almost always be in the form `Deque &operator=(const Deque &other)`. That is you take a reference and you return a reference to this after having updated it. – Phil1970 Dec 12 '17 at 01:23
  • @Phil1970 I fixed the assignment operator. – Jake Freeman Dec 12 '17 at 01:27
  • Not completely... You return a reference to a variable on the stack which is **undefined behavior** and the object itself it not modified. – Phil1970 Dec 12 '17 at 01:28
  • @Phil1970 sorry I am not great with assignment operator overloading, I think I fixed it but please let me. – Jake Freeman Dec 12 '17 at 01:32
  • Thank you everyone for your help, I have everything working except the "<<" operator. Following @Jake Freeman 's advice I changed the prototype in the function to "friend std::ostream & operator<<(std::ostream & x, Deque n);" But I keep getting the error: 'Deque' is not a type. What am I missing? – Schnagl Dec 12 '17 at 01:34
  • @Schnagl did you place the implementation before the declaration of the class? Also the `friend` tag should not be in the definition of the function. – Jake Freeman Dec 12 '17 at 01:36
  • @Jake Freeman, No, I implemented it right after the class, and I have friend only in the declaration in the class. In the class I have: "friend std::ostream & operator<<(std::ostream & cout, Deque d);" Outside in the function declaration I have: "std::ostream & operator<<(std::ostream & cout, Deque d) { return cout; }" ... sorry for the poor comment formatting – Schnagl Dec 12 '17 at 01:48
  • @Schnagl do you have `template` behind it and it should be `std::ostream & operator<<(std::ostream & cout, Deque& d)` – Jake Freeman Dec 12 '17 at 01:49
  • @JakeFreeman I have that now, but I am still getting "warning: friend declaration ‘std::ostream& operator<<(std::ostream&, Deque)’ declares a non-template function " and "note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)" – Schnagl Dec 12 '17 at 02:08
  • After a cursory search on SO it appears that is a common warning but does not affect the outcome. @Schnagl – Jake Freeman Dec 12 '17 at 02:11