Reputation: 9466
I'm trying to compile the code from A Tour of C++, Chapter 4.3, which describes "Abstract Types". However, I am getting a compiler error:
$ g++ -std=c++11 abstract.cpp Vector.cpp Vector_container.cpp
/tmp/ccToFsNS.o: In function `g()':
abstract.cpp:(.text+0x9c): undefined reference to `Vector_container::Vector_container(std::initializer_list<double>)'
abstract.cpp:(.text+0xb4): undefined reference to `Vector_container::~Vector_container()'
abstract.cpp:(.text+0xc5): undefined reference to `Vector_container::~Vector_container()'
collect2: error: ld returned 1 exit status
How can I fix my code so it compiles? I am brand new to C++ and am trying to transcribe Stroustrup's examples to the best of my ability.
abstract.cpp:
#include "Vector_container.hpp"
#include <iostream>
void use(Container& c)
{
const int sz = c.size();
for (auto i = 0; i != sz; ++i)
{
std::cout << c[i] << "\n";
}
}
void g()
{
Vector_container vc {10, 9, 8, 7, 6};
use(vc);
}
int main()
{
g();
}
Vector_container.hpp:
#include "Container.hpp"
#include "Vector.hpp"
#include <initializer_list>
#pragma once
class Vector_container : public Container {
Vector v;
public:
Vector_container(int);
Vector_container(std::initializer_list<double>);
~Vector_container();
double& operator[](int);
int size() const;
};
Vector_container.cpp:
#include "Container.hpp"
#include "Vector.hpp"
class Vector_container : public Container {
Vector v;
public:
Vector_container(int s) : v(s) {}
Vector_container(std::initializer_list<double> lst) : v{lst} {}
~Vector_container() {}
double& operator[](int i) { return v[i]; }
int size() const { return v.size(); }
};
container.hpp:
#pragma once
class Container {
public:
virtual double& operator[](int) = 0;
virtual int size() const = 0;
virtual ~Container() {}
};
Vector.hpp:
#include <initializer_list>
#pragma once
class Vector {
public:
Vector(int);
Vector(std::initializer_list<double>);
~Vector();
double& operator[](int);
int size() const;
void push_back(double);
private:
double* elem;
int sz;
};
Vector.cpp:
#include "Vector.hpp"
#include <stdexcept>
Vector::Vector(int s) : elem{new double[s]}, sz{s}
{
if (s < 0)
{
throw std::length_error{"Size must be >= 0"};
}
for (auto i = 0; i != s; ++i)
{
elem[i] = 0;
}
}
Vector::Vector(std::initializer_list<double> lst)
: elem{new double[lst.size()]}, sz{static_cast<int>(lst.size())}
{
std::copy(lst.begin(), lst.end(), elem);
}
Vector::~Vector() {
delete[] elem;
}
double& Vector::operator[](int i)
{
if (i < 0 || size() <= i)
{
throw std::out_of_range{"Vector::operator[]"};
}
return elem[i];
}
int Vector::size() const
{
return sz;
}
Upvotes: 0
Views: 1227
Reputation: 9466
As noted by commenters and Sam, I was not defining Vector_container
correctly. I changed Vector_container.cpp to:
#include "Vector_container.hpp"
Vector_container::Vector_container(int s) : v(s) {}
Vector_container::Vector_container(std::initializer_list<double> lst) : v{lst} {}
Vector_container::~Vector_container() {}
double& Vector_container::operator[](int i)
{
return v[i];
}
int Vector_container::size() const
{
return v.size();
}
And now the program compiles and runs as expected.
Upvotes: 1
Reputation: 118352
I have not read that particular book, but I would be surprised to learn that Stroustrup would write something that:
1) Explicitly depends on gcc-specific pragmas, and
2) Encourage the abuse of #pragma once
3) Offers an example of this kind of bad practice of declaring a class in the header file, but then define the class's methods in a completely separate translation unit that does not even include the header file.
Just get rid of your Vector_container.hpp
entirely, then rename Vector_container.cpp
to Vector_container.hpp
, and the code should compile and link just fine.
Your problem is that nothing in the existing Vector_container.cpp
translation unit actually ends up instantiating and compiling the class methods, in order for the linker to find and resolve the references from your other translation units.
You could also replace the inline definitions in Vector_container.cpp
with non-inline definitions. This will also instantiate them. But this won't do anything about the duplicate definition in Vector_container.hpp
, which I think is very bad practice.
Upvotes: 0