lundi 28 février 2022

why C++ header and implementation files do not link? [duplicate]

I am implementing a simple Stack class and when I include the header file into my main.cpp file there are undefined reference error for every function that I used in main function. But move the implementations from "stack.cpp" to the end of "stack.h", and it works.
These are the files from the project:
stack.h

#ifndef STACK_H
#define STACK_H

#include <exception>
#include <string>
#include <initializer_list>

class StackException : public std::exception {
public:
    StackException() = default;
    explicit StackException(const std::string& msg) : message{msg} {}
    explicit StackException(const char* msg) : message{msg} {}
    virtual ~StackException() = default;

    virtual const char* what(void) const noexcept { return message.c_str(); }
private:
    std::string message;
};

template<typename T>
class Stack {
public:
    Stack(const uint64_t& = 8);
    Stack(const std::initializer_list<T>&);
    Stack(const Stack<T>&);
    Stack(const Stack<T>&&) noexcept;
    ~Stack();

    T& top(void) const;
    void push(const T&);
    const T& pop(void);
    bool isEmpty(void) const;
    bool isFull(void) const;
    const uint64_t& size(void) const;

    Stack& operator=(const Stack&);
    bool operator==(const Stack&) const;
private:
    T* _arr;
    uint64_t _size;
    uint64_t _allocated;
    void reset(void);
    void extend(void);
    void shrink(void);
};

#endif // STACK_H

stack.cpp

#include "stack.h"

template<typename T>
Stack<T>::Stack(const uint64_t& capacity)
{
    _size = 0;
    _allocated = capacity;
    _arr = new T[_allocated];
}

template<typename T>
Stack<T>::Stack(const std::initializer_list<T>& il) : Stack() {
   for(T item : il) insert(item);
}

template<typename T>
Stack<T>::Stack(const Stack<T>& other) : Stack{other._allocated} {
    _size = other._size;
    for(int i = 0; i < _allocated; i++) _arr[i] = other._arr[i];
}

template<typename T>
Stack<T>::Stack(const Stack<T>&& other) noexcept {
    _size = other.size;
    _allocated = other._allocated;
    _arr = other._arr;
    other._arr = nullptr;
    other.reset();
}

template<typename T>
Stack<T>::~Stack(void) {
    reset();
}

template<typename T>
void Stack<T>::reset(void) {
    if(_arr != nullptr) {
        delete[] _arr;
        _arr = nullptr;
    }
    _size = 0;
    _allocated = 0;
}

template<typename T>
void Stack<T>::extend(void) {
    Stack<T> temp{std::move(*this)};
    _arr = new T[temp._allocated * 2];
    _allocated = temp._allocated * 2;
    _size = temp._size;

    for(int i = 0; i < temp._allocated; i++) _arr[i] = temp._arr[i];
}

template<typename T>
void Stack<T>::shrink(void) {
    Stack<T> temp{std::move(*this)};
    _arr = new T[temp._allocated / 2];
    _allocated = temp._allocated / 2;
    _size = temp._size;

    for(int i = 0; i < _allocated; i++) _arr[i] = temp._arr[i];
}

template<typename T>
T& Stack<T>::top() const {
    if(isEmpty()) throw StackException("Stack is empty!");
    return _arr[_size - 1];
}

template<typename T>
void Stack<T>::push(const T& item) {
    if(isFull()) extend();
    _arr[_size++] = item;
}

template<typename T>
const T& Stack<T>::pop(void) {
    if(isEmpty()) throw StackException("Stack is empty!");
    if(_size <= _allocated / 4) shrink();
    T& temp = _arr[--_size];
    return temp;
}

template<typename T>
bool Stack<T>::isEmpty(void) const {
    return _size == 0;
} 

template<typename T>
bool Stack<T>::isFull(void) const {
    return _size >= _allocated;
}

template<typename T>
const uint64_t& Stack<T>::size(void) const {
    return _size;
}

template<typename T>
Stack<T>& Stack<T>::operator=(const Stack<T>& other) {
    if(&other == this) return *this;

    if(other._allocated != _allocated) {
        delete[] _arr;
        _arr = new T[other._allocated];
        _allocated = other._allocated;
    }
    _size = other.size;
    for(int i = 0; i < _allocated; i++) _arr[i] = other._arr[i];

    return *this;
}

template<typename T>
bool Stack<T>::operator==(const Stack<T>& other) const {
    if(_size != other._size) return false;

    for(int i = 0; i < _size; i++) {
        if(_arr[i] != other._arr[i]) return false;
    }

    return true;
}

main.cpp

#include <iostream>
#include "stack.h"
using namespace std;

int main() {
    Stack<int> test(8);
    test.push(3);
    cout << test.size() << endl;
    return 0;
}

In "main.cpp" I replaced #include "stack.h" with #include "stack.cpp and it worked without error. Using g++ stack.cpp main.cpp -o a outputs the same errors when I use #include "stack.h".
This is certainly not my first time separating headers and implementations in multiple files but this is quite new to me. So shall I go on and use #include "stack.cpp" (which I don't suppose is a good idea)? Is there any reason to this?

Aucun commentaire:

Enregistrer un commentaire