snippetcppMinor
Implement 3 stacks using a single array in C++
Viewed 0 times
implementarraystackssingleusing
Problem
One of the questions that is presented in Chapter 3 for Cracking the Coding Interview is this:
I've implemented a solution to this problem in C++. My assumption is that the array is a static array that is not growable (unlike a dynamic array).
I would like the focus be not only on the answer's correctness, but also other stuff pertaining to proper design of a generic C++ container. Including but not limited to exception guarantees, proper implementation of Rule of Five, space/time efficiency, handling all possible types for T (including if T has no default constructor), coding style and whatever else you think an interviewer may look for when asking this question.
Code
```
#pragma once
template
class ArrayStack
{
public:
ArrayStack(int size = 100);
ArrayStack(const ArrayStack& other);
ArrayStack(ArrayStack&& other);
~ArrayStack();
ArrayStack& operator= (const ArrayStack& other);
ArrayStack& operator= (ArrayStack&& other);
friend void swap(ArrayStack& A, ArrayStack& B)
{
using std::swap;
swap(A.arr, B.arr);
swap(A.arrSize, B.arrSize);
swap(A.stack1Index, B.stack1Index);
swap(A.stack2Index, B.stack2Index);
swap(A.stack3Index, B.stack3Index);
}
void push(T item, int stackNum);
void pop(int stackNum);
T& top(int stackNum);
private:
T* arr;
size_t stack1Index;
size_t stack2Index;
size_t stack3Index;
size_t arrSize;
};
template
ArrayStack::ArrayStack(int size)
:arr(static_cast(::operator new(sizeof(T)*size)))
, arrSize(size)
, stack1Index(0)
, stack2Index(size / 3)
, stack3Index(2 * size / 3)
{}
template
ArrayStack::ArrayStack(const ArrayStack& other)
:arr(static_cast(::operator new(sizeof(T)*other.arrSize)))
, arrSize(other.arrSize)
, stack1Index(other.stack1Index)
, stack2Index(other.stack2Index)
, stack3Index(other.stack3Index)
{
Describe how you could use a single array to implement 3 stacksI've implemented a solution to this problem in C++. My assumption is that the array is a static array that is not growable (unlike a dynamic array).
I would like the focus be not only on the answer's correctness, but also other stuff pertaining to proper design of a generic C++ container. Including but not limited to exception guarantees, proper implementation of Rule of Five, space/time efficiency, handling all possible types for T (including if T has no default constructor), coding style and whatever else you think an interviewer may look for when asking this question.
Code
```
#pragma once
template
class ArrayStack
{
public:
ArrayStack(int size = 100);
ArrayStack(const ArrayStack& other);
ArrayStack(ArrayStack&& other);
~ArrayStack();
ArrayStack& operator= (const ArrayStack& other);
ArrayStack& operator= (ArrayStack&& other);
friend void swap(ArrayStack& A, ArrayStack& B)
{
using std::swap;
swap(A.arr, B.arr);
swap(A.arrSize, B.arrSize);
swap(A.stack1Index, B.stack1Index);
swap(A.stack2Index, B.stack2Index);
swap(A.stack3Index, B.stack3Index);
}
void push(T item, int stackNum);
void pop(int stackNum);
T& top(int stackNum);
private:
T* arr;
size_t stack1Index;
size_t stack2Index;
size_t stack3Index;
size_t arrSize;
};
template
ArrayStack::ArrayStack(int size)
:arr(static_cast(::operator new(sizeof(T)*size)))
, arrSize(size)
, stack1Index(0)
, stack2Index(size / 3)
, stack3Index(2 * size / 3)
{}
template
ArrayStack::ArrayStack(const ArrayStack& other)
:arr(static_cast(::operator new(sizeof(T)*other.arrSize)))
, arrSize(other.arrSize)
, stack1Index(other.stack1Index)
, stack2Index(other.stack2Index)
, stack3Index(other.stack3Index)
{
Solution
Don't use
Don't catch every exception, ignoring the type
It's not good practice to catch every exception that can happen (i.e.
Don't use
There is no need to create every exception on the heap when throwing them. This results in memory leak if the you forget to
Provide a
If I have a
If you don't want to implement the logic twice, there is an "elegant" solution to this.
Copy constructor doesn't allow the parameter to be move constructed
Because your copy constructor is taking by
Hope that helps! :)
system("pause")system("pause") doesn't work on Linux (although I don't think it matters, the console stays open), and so your code is not cross-platform. Please see this for additional details.Don't catch every exception, ignoring the type
It's not good practice to catch every exception that can happen (i.e.
catch (...)). Rather, use a const& to the type of the exception that should be handled://Catches exceptions thrown by new
catch(const std::bad_array_new_length&)
//...Don't use
new when throwing exceptionsThere is no need to create every exception on the heap when throwing them. This results in memory leak if the you forget to
delete the exception in the catch clause, and should be avoided, just like you would avoid using int* a = new int(); every time you want an integer :) Here is some further reading//throw a regular object, not a pointer
throw std::runtime_error("No such stack");Provide a
const top() functionIf I have a
const ArrayStack, I can't do anything with it, not even get the top element. This is probably not intended, so consider making a const version of top().If you don't want to implement the logic twice, there is an "elegant" solution to this.
Copy constructor doesn't allow the parameter to be move constructed
Because your copy constructor is taking by
const&, the compiler can't optimize and move construct tmp when appropriate, instead it will call the constructor, possibly creating a new object, which is unnecessary overhead. Passing by value fixes this problem.template
ArrayStack& ArrayStack::operator =(ArrayStack other);Hope that helps! :)
Code Snippets
//Catches exceptions thrown by new
catch(const std::bad_array_new_length&)
//...//throw a regular object, not a pointer
throw std::runtime_error("No such stack");template <typename T>
ArrayStack<T>& ArrayStack<T>::operator =(ArrayStack<T> other);Context
StackExchange Code Review Q#136626, answer score: 6
Revisions (0)
No revisions yet.