I had refrenced a few other similar questions, they would often point to circularity being the problem. But I cannot see that anywhere within my code.
arrayi.cpp:
#include "arrayi.h"
// Member function definitions for class Array
// Initialize static data member at file scope
template<typename T>
int Array<T>::arrayCount = 0; // no objects yet
// Default constructor for class Array
template<typename T>
Array<T>::Array(int arraySize)
{
++arrayCount; // count one more object
size = arraySize; // default size is 10
ptr = new int[size]; // create space for array
assert(ptr != 0); // terminate if memory not allocated
int i;
for (i = 0; i < size; i++)
ptr[i] = 0; // initialize array
}
arrayi.h:
#ifndef ARRAYI_H_
#define ARRAYI_H_
#include <iostream>
#include <cstdlib>
#include <cassert>
using namespace::std;
template<typename T> class Array;
template<typename T>
ostream &operator<< (ostream& output, const Array<T> &a);
template<typename T>
class Array
{
friend ostream &operator<< <>(ostream &output, const Array<T> &a);
public:
Array(int = 10); //constructor
Array(const Array &); //copy constructor
private:
int *ptr; //ptr to first array element
int size; //size of the array
static int arrayCount; // #of arrays instantiated
};
#include "arrayi.t"
#endif
arrayi.t:
#ifndef ARRAYI_T_
#define ARRAYI_T_
#include <iostream>
#include <cstdlib>
#include <cassert>
using namespace::std;
// Default constructor for class Array
template<typename T>
Array<T>::Array(int arraySize)
{
cout << "calling the constructor \n";
}
// Overloaded output operator for class Array
template<typename T>
ostream &operator<<(ostream &output, const Array<T> &a)
{
int i;
output << "{ ";
for (i = 0; i < a.size; i++)
{
output << a.ptr[i] << ' ';
if ((i + 1) % 10 == 0)
output << "}" << endl;
} //end for
if (i % 10 != 0)
output << "}" << endl;
return output; // enables cout << x << y;
}
#endif
I have been scanning my code up and down for quite some hours now, so any help would be much appreciated, thanks in advance! Any messy or broken looking code may be because this is from a work in progress, but there are no errors but the mentioned one at the moment. Everything shown compiles when the function "Array::Array(int arraySize)" is removed.
arrayi.t defines
Array<T>::Array(int arraySize)
{
cout << "calling the constructor \n";
}
arrayi.cpp defines
template<typename T>
Array<T>::Array(int arraySize)
{
++arrayCount; // count one more object
size = arraySize; // default size is 10
ptr = new int[size]; // create space for array
assert(ptr != 0); // terminate if memory not allocated
int i;
for (i = 0; i < size; i++)
ptr[i] = 0; // initialize array
}
Two definitions of the same function with the same parameters is not allowed.
Solution:
Pick the one that is the real constructor. Delete the other one. If you select the implementation in arrayi.cpp, ensure that it is visible to translation units that require it. This most likely means move it to arrayi.t.
Give Template static variable a read for heads-up on another problem coming your way.
Related
I was trying to define my own class template Array<T> to practice the usage of templates.
The code I produced builds properly, but when executed it gives the following error
terminate called after throwing an instance of 'std::bad_array_new_length'
what(): std::bad_array_new_length
I think I have found a solution to the problem, but I would be interested to see if there was an underlying error in the previous code and if so, which one.
This is the code I previously wrote:
#include <iostream>
class Empty{
private:
char error;
public:
Empty(char e) : error(e) { std::cout << "Azione non disponibile, lista vuota" << std::endl;}
};
template <class T>
class Array;
template <class T>
std::ostream& operator<<(std::ostream&,const Array<T>&);
template <class T>
class Array{
friend std::ostream& operator<< <T> (std::ostream&,const Array<T>&);
private:
T* arr;
unsigned int size;
unsigned int capacity;
static T* copia(T* a, unsigned int s, unsigned int c){
if(c > 0) {
T* app = new T[c];
for (int i = 0; i<s; ++i) {
app[i] = a[i];
}
return app;
}else return nullptr;
}
public:
Array(int k = 0, const T& t = T()) : size(k > 0 ? k : 0), capacity(size), arr(k > 0 ? new T[size] : nullptr){
for (int i = 0; i < k ; ++i) arr[i] = t;
}
Array(const Array& a) : size(a.size), capacity(a.capacity), arr(copia(a.arr,a.size,a.capacity)){}
Array& operator=(const Array& a){
if(this != &a){
delete[] arr;
capacity = size = a.size;
arr = copia(a.arr,a.size,a.capacity);
}
return *this;
}
~Array(){delete[] arr;}
void pushBack(const T& t) {
if(size == capacity){
capacity > 0 ? capacity *= 2 : capacity = 1;
T* app = copia(arr,size, capacity);
delete[] arr;
arr = app;
}
++size;
arr[size-1] = t;
}
T popBack() {
if (size != 0) {
T temp = arr[size - 1];
--size;
return temp;
} else throw Empty('e');
}
};
template <class T>
std::ostream& operator<<(std::ostream& os ,const Array<T>& a){
for (int i = 0; i < a.size; ++i) {
os << a.arr[i] << ' ';
}
std::cout << std::endl;
return os;
}
int main(){
Array<int> a(5,5),e;
std::cout << a << std::endl;
a.pushBack(16);
a.pushBack(17);
a.pushBack(18);
std::cout << a << std::endl;
return 0;
}
If I run this code without the a.pushBack(x) function call, it works.
As soon as I insert even one function call, I get that error in the output.
While debugging, I realized that the line where I had written T* arr was not the correct one.
Knowing that the constructor follows the order of initialization of its own sub-objects, the first element to be constructed is the pointer.
Since I'm trying to create a vector of elements of T with dimension size, rightly gives me the error, as I have not yet initialized the integer size.
So I solved it by swapping the lines.
template <class T>
class Array{
friend std::ostream& operator<< <T> (std::ostream&,const Array<T>&);
private:
unsigned int size;
unsigned int capacity;
T* arr;
...
};
But at this point I wonder: why, if I don't make the function call, I don't get the same error, knowing that size at the time of construction is undefined?
Logically, the problem should also occur in that case, but everything seems to work:
PS: Don't count on the fact that I didn't handle the exception being thrown, the code is not yet fully complete, but for the moment I was keen to at least implement the Rule of Three.
Compiling with GCC 9.1.0 in jdoodle.com, I consistently got a bad_alloc runtime exception with your original code.
I added a new constructor with a different signature so I could see what value of size it was using to allocate the array
Note: Even the existence of this new ctor prevented the bad_alloc error, whether it was called or not.
Array(char c, int k = 0, const T& t = T()) :
size(k > 0 ? k : 0),
capacity(size),
arr(DebugInit(size)){
for (int i = 0; i < k ; ++i) arr[i] = t;
}
T* DebugInit( unsigned long size_init )
{
std::cout << "DebugInit size_init=" << size_init << " cap=" << capacity << std::endl;
return size_init > 0 ? new T[size_init] : nullptr;
}
The results seemed to be random when using this new ctor.
size_init varied each time, which is consistent with using a member field which has not yet been initialised.
In the original code, it may be that size happened to consistently have 0 in it.
By adding more code, even if it was never called, the compiled version would then access a random but now non-zero value for size.
It seems like classic "undefined behaviour". If you use size before it is initialised, there are no guarantees about what will be in it.
If you're lucky, size will consistently return 0 before it is initialised and you'll get an allocation error.
But a small change to the code may start returning random values for size.
If the uninitialised size has a much larger value than the one you intended, you won't see a problem until later, or maybe never.
After a bit more playing around with the code, the std::bad_alloc exception came back! So, definitely no guarantees!
I am trying to instantiate my sarray class.
sarray array(10);
But I get an error saying that their is no constructor which has a matching definition.
The constructor is defined in the sarray header file
#ifndef SARRAY_H
#define SARRAY_H
template <class T>
class sarray {
public:
template<class T>sarray(int size);
~sarray();
template<class T> T& operator[](int i);
private:
int size;
T* data;
};
And is implemented in the sarray cpp file:
template<class T> sarray::sarray(int size)
: size(size)
{
if (size > 0) data = new T[size];
else {
cout << "illegal array size = " << size << endl;
exit(1);
}
}
Do I need to specify that I am using templates in the constructor call? The constructor argurments and the call arguments match so I am confused by the error message.
VS Syntax Error Message
C++ argument list for class template is missing
Full Source Code
#include "sarray.h"
#include <iostream>;
using namespace std;
template<class T> sarray::sarray(int size)
: size(size)
{
if (size > 0) data = new T[size];
else {
cout << "illegal array size = " << size << endl;
exit(1);
}
}
sarray::~sarray()
{
delete[] data;
}
template<class T> T& sarray::operator[](int i)
{
if (i < 0 || i >= size) {
cout << "index " << i << " is out of bounds." << endl;
exit(1);
}
return data[i];
}
The template<class T> on top of you class affects the whole class. You do not need to keep adding template<class T> in front of all methods of the class. You do need to do it in the definitions of the methods, when you are writing them outside the class. So your class should become:
template <class T>
class sarray {
public:
sarray(int size);
~sarray();
T& operator[](int i);
private:
int size;
T* data;
};
And then the definitions of the methods would look like:
template<class T> sarray<T>::sarray(int size)
: size(size)
{
if (size > 0) data = new T[size];
else {
cout << "illegal array size = " << size << endl;
exit(1);
}
}
template<class T> sarray<T>::~sarray()
{
delete[] data;
}
template<class T> T& sarray<T>::operator[](int i)
{
if (i < 0 || i >= size) {
cout << "index " << i << " is out of bounds." << endl;
exit(1);
}
return data[i];
}
However, as people have commented, it is not a good idea to define the methods of a template class in a separate .cpp file. You could look around the internet for the full explanation why, as it would be too long for here.
If you still want them to be out of the class' declaration (but still in the same header file) for e.g. better readability, then you could do it the shown way.
I have a program that I'm writing that has an abstract base class "AssortedSorted", a derived class "BubbleSort", and a a class to test sorts "AssortedSorterTest".
The Idea is to create a bubbleSort instance, pass that instance to an instance of assortedSorterTest along with an int for the quantity of random numbers to create and sort, and return true from assortedsorter.testSort() method if the vector is sorted and contains the same number of elements as the vector it was given.
If you read the code, there are things that need to be changed to accomplish this, but I'm not concerned with correcting those yet unless they are relevant to the issue I am currently having regarding invalid initialization on line 16 in main.cpp. The error that I am getting is this
"invalid initialization of non-const reference of type 'AssortedSorter&' from an rvalue of type BubbleSort*"
Initially I thought that adding #include "BubbleSort.h" to the AssortedSorterTest class may correct the problem, it did not. I've also tried changing some of the references to pointers, this created new problems for me, so I switched back to references. I haven't had any luck in figuring this out, so any healp would be appreciated.
#pragma once
#include <vector>
#include <string>
class AssortedSorter
{
public:
virtual std::vector<int> sort(const std::vector<int> &itemsToSort) = 0;
virtual std::string getName() const = 0;
virtual ~AssortedSorter() {};
};
#include <sstream>
class BubbleSort : public AssortedSorter
{
private:
long loopCount{0};
long swapCount{0};
public:
BubbleSort();
~BubbleSort() override;
std::vector<int> sort(const std::vector<int> &itemsToSort) override;
std::string getName() const override;
friend std::ostream &operator<<(std::ostream &out, const BubbleSort &rhs);
};
#include "BubbleSort.h"
BubbleSort::BubbleSort()
{
}
BubbleSort::~BubbleSort()
{
}
std::vector<int> BubbleSort::sort(const std::vector<int> &itemsToSort)
{
std::vector<int> itemsSorted = itemsToSort;
bool swap{false};
int temporary_num{};
do
{
swap = false;
for (int index = 0; index < itemsSorted.size()-1; index++)
{
loopCount++;
if (itemsSorted[index] > itemsSorted[index + 1])
{
swapCount++;
temporary_num = itemsSorted[index];
itemsSorted[index] = itemsSorted[index + 1];
itemsSorted[index + 1] = temporary_num;
swap = true;
}
}
} while (swap);
return itemsSorted;
}
std::string BubbleSort::getName() const
{return "BubbleSort";}
//Overloaded insertion operator
std::ostream &operator<<(std::ostream &os, const BubbleSort &rhs)
{
os << rhs.getName() << ": " << std::to_string(rhs.loopCount) << " " << std::to_string(rhs.swapCount);
return os;
}
#pragma once
#include "AssortedSorter.h"
#include <vector>
class AssortedSorterTest
{
public:
AssortedSorterTest();
~AssortedSorterTest();
bool testSort(AssortedSorter &assortedSorter, int size);
};
#include "AssortedSorterTest.h"
AssortedSorterTest::AssortedSorterTest()
{
}
AssortedSorterTest::~AssortedSorterTest()
{
}
bool testSort(AssortedSorter &assortedSorter, int size)
{
std::vector<int> randomNumbers;
for(int index{0}; index < size; index++)
{
randomNumbers.push_back(rand());
}
std::vector<int> sortedVector = assortedSorter.sort(randomNumbers);
if(sortedVector == randomNumbers)
{
return true;
}
else
{
return false;
}
}
#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>
#include "AssortedSorterTest.h"
#include "BubbleSort.h"
std::vector<int> assign_vector_values(int size);
int main()
{
std::vector<int> vec = assign_vector_values(100);
AssortedSorter &bubbleSort = new BubbleSort; //problem is here
AssortedSorterTest sortTester;
if(sortTester.testSort(bubbleSort, 100))
{
std::cout << "Vector has been sorted" << std::endl;
}
else
{
std::cout << "Vector has not been sorted properly" << std::endl;
}
delete bubbleSort;
return 0;
}
std::vector<int> assign_vector_values(int size)
{
std::vector<int> temp_vector;
for(int index{0}; index < size; index++)
{
temp_vector.push_back(rand());
}
return temp_vector;
}
The error message is telling you exactly what the issue is.
new BubbleSort results in a pointer to a BubbleSort.
You are trying to bind a reference to the base class of BubbleSort to it. That cannot work.
Either you need to dereference the pointer or you need to initialize a pointer, not a reference, with it.
In any case you should not use naked new/delete in modern C++. Use std::unique_ptr<AssortedSorter> and std::make_unique<BubbleSort>() instead:
std::unique_ptr<AssortedSorter> bubbleSort = std::make_unique<BubbleSort>();
This requires #include<memory>.
Or, given how exactly the code in main looks right now, there is no need for a dynamic allocation at all. Simply
BubbleSort bubbleSort;
will do as well.
I am trying to implement a templated class that has a vector container from the standard library as a member. The purpose of the class is to create a linear algebra class for my personal use.
The class is supposed to have a constructor that initializes a vector filled only with zeros and two overloadings of the [] operator to access the data.
The code looks as follows:
#include <vector>
#include <iostream>
template<class T>class LinAlg
{
private:
std::vector<T> mVector;
int mSize;
public:
LinAlg(int size);
T & operator[](int i);
T const & operator[] (int i)const;
};
template<class T> LinAlg<T>::LinAlg(int size)
{
mSize = size;
std::vector<T> mVector;
for (int i=0; i<mSize; i++)
{
mVector.push_back(0);
}
}
template<class T> T & LinAlg<T>::operator[](int i)
{
return mVector[i];
}
template<class T> T const& LinAlg<T>::operator[](int i)const
{
return mVector[i];
}
int main()
{
LinAlg<double> vec(2);
vec[0] = 1.0;
vec[1] = 1.0;
for(int i=0; i<2; i++)
{
std::cout << vec[i] << '\n';
}
return 0;
}
Apparently, the code compiles correctly, but there is no output from the main function. The problem appears to be in the overloading of the operators and I have been reading in different forums but I haven't been able to find the solution.
Any help is appreciated.
In
template<class T> LinAlg<T>::LinAlg(int size)
{
mSize = size;
std::vector<T> mVector; // <- oops
for (int i=0; i<mSize; i++)
{
mVector.push_back(0);
}
}
std::vector<T> mVector; defines a new variable mVector that shadows the member variable mVector. The local mVector effectively replaces the member mVector and is initialized, loaded with 0s and then discarded when the constructor ends. The member mVector is left default initialized to size zero, causing problems later on when you attempt to access the values you believe you stored.
Instead you could
template<class T> LinAlg<T>::LinAlg(int size)
{
mSize = size;
// std::vector<T> mVector; <- remove this
for (int i=0; i<mSize; i++)
{
mVector.push_back(0);
}
}
but std::vector has a constructor that does this work for you, default initializing size elements, that you can take advantage of in the Member Initializer List
template<class T> LinAlg<T>::LinAlg(int size):
mVector(size), mSize(size)
{
}
Further, since std::vector knows its length, you can remove the mSize member and replace uses of it with mVector.size()
In your constructor, you are declaring a local variable mVector that hides the class member of the same name. So you are populating the local variable, not the class member. You need to remove the local variable:
template<class T> LinAlg<T>::LinAlg(int size)
{
mSize = size;
for (int i=0; i<mSize; i++)
{
mVector.push_back(0);
}
}
Now, that being said, you don't need the loop at all, since std::vector has a resize() method that can fill the vector with values:
template<class T> LinAlg<T>::LinAlg(int size)
{
mSize = size;
mVector.resize(size); // <-- will set new elements to 0 by default
}
Alternative, you can use the vector's own constructor instead, which does the same thing:
template<class T> LinAlg<T>::LinAlg(int size)
: mVector(size) // <-- will set elements to 0 by default
{
mSize = size;
}
Either way, the mSize member is redundant, as std::vector has its own size() method that you can use when needed.
I have a program that uses something I made called "SortableVector," with a parent called SimpleVector. The Simple Vector is fine, and so is most of SortableVector, but the problem is the sort function in SortableVector. It sorts numbers just fine, but I can't get it to sort strings. Here's what I originally had:
template <class T>
void SortableVector<T>::sort()
{
T temp = 0;
for(int i = 0; i < this->size(); i++)
{
for (int count = i+1; count < this->size(); count++)
{
if(this->operator[](0) == int)
{
if (this->operator[](count) < this->operator[](i))
{
temp = this->operator[](count);
this->operator[](count) = this->operator[](i);
this->operator[](i) = temp;
count = i+1;
}
}
}
}
}
Then I tried to use the sort(begin, end) function, but that didn't work either:
template <class T>
void SortableVector<T>::sort()
{
sort(this->operator[].begin(), this->operator[].end();
}
EDIT: To help people understand the problem, here's the whole file:
#ifndef SORTABLEVECTOR_H
#define SORTABLEVECTOR_H
using namespace std;
#include "SimpleVector.h"
#include <algorithm>
#include <fstream>
#include <string>
template <class T>
class SortableVector : public SimpleVector<T>
{
public:
SortableVector(int s) : SimpleVector<T>(s)
{}
SortableVector(SortableVector &);
SortableVector(SimpleVector<T> &obj):
SimpleVector<T>(obj)
{}
void sort();
};
template <class T>
SortableVector<T>::SortableVector(SortableVector &obj):SimpleVector<T>(obj)
{
}
template <class T>
void SortableVector<T>::sort()
{
std::sort(this->operator[].begin(), this->operator[].end());
T temp = 0;
for(int i = 0; i < this->size(); i++)
{
for (int count = i+1; count < this->size(); count++)
{
if(this->operator[](0) == int)
{
if (this->operator[](count) < this->operator[](i))
{
temp = this->operator[](count);
this->operator[](count) = this->operator[](i);
this->operator[](i) = temp;
count = i+1;
}
}
}
}
}
#endif
And this is SimpleVector:
// SimpleVector.h
#ifndef SIMPLEVECTOR_H
#define SIMPLEVECTOR_H
#include <iostream>
#include <cstdlib>
using namespace std;
template <class T>
class SimpleVector
{
private:
T *aptr;
int arraySize;
void subError(); // Handles subscripts out of range
public:
SimpleVector() // Default constructor
{ aptr = 0; arraySize = 0;}
SimpleVector(int); // Constructor
SimpleVector(const SimpleVector &); // Copy constructor
~SimpleVector(); // Destructor
int size() { return arraySize; }
T &operator[](int); // Overloaded [] operator
void print() const; // outputs the array elements
void push_back(T); // newly added function (implemention needed)
T pop_back(); // newly added function (implemention needed)
};
//****************************************************************
// Constructor for SimpleVector class *
// Sets the size of the array and allocates memory for it. *
//****************************************************************
template <class T>
SimpleVector<T>::SimpleVector(int s)
{
arraySize = s;
aptr = new T [s];
}
//*********************************************
// Copy Constructor for SimpleVector class *
//*********************************************
template <class T>
SimpleVector<T>::SimpleVector(const SimpleVector &obj)
{
arraySize = obj.arraySize;
aptr = new T [arraySize];
for(int count = 0; count < arraySize; count++)
*(aptr + count) = *(obj.aptr + count);
}
// *************************************
// Destructor for SimpleVector class *
// *************************************
template <class T>
SimpleVector<T>::~SimpleVector()
{
if (arraySize > 0)
delete [] aptr;
}
//************************************************************
// SubError function *
// Displays an error message and terminates the program when *
// a subscript is out of range. *
//************************************************************
template <class T>
void SimpleVector<T>::subError()
{
cout << "ERROR: Subscript out of range.\n";
exit(0);
}
//***********************************************************
// Overloaded [] operator *
// This function returns a reference to the element *
// in the array indexed by the subscript. *
//***********************************************************
template <class T>
T &SimpleVector<T>::operator[](int sub)
{
if (sub < 0 || sub >= arraySize)
subError();
return aptr[sub];
}
//********************************************************
// prints all the entries is the array. *
//********************************************************
template <class T>
void SimpleVector<T>::print( ) const
{
for (int k = 0; k < arraySize; k++ )
cout << aptr[k] << " ";
cout << endl;
}
//***************************************************************
// (1) push_back(T val) *
// The push_back function pushes its argument onto the back *
// Of the vector. *
//***************************************************************
template <class T>
void SimpleVector<T>::push_back(T val)
{
aptr[arraySize] = val;
arraySize++;
}
// *****************************************************
// (2) pop_back() *
// The pop_back function removes the last element *
// Of the vector. It also returns that value. *
// *****************************************************
template <class T>
T SimpleVector<T>::pop_back()
{
arraySize--;
T temp = aptr[arraySize];
return temp;
}
#endif
The SimpleVector was provided by the instructor.
You can do without the repeated this-> 99% of the time
You can just invoke operators, instead of invoking them as functions
You can just use std::sort from <algorithm>
Imagining some of the code you didn't show:
#include <vector>
#include <algorithm>
template <typename T>
struct SimpleVector {
std::vector<T> data;
virtual ~SimpleVector() {}
};
template <typename T>
struct SortableVector : public SimpleVector<T> {
void sort() {
std::sort(this->data.begin(), this->data.end());
}
};
#include <iostream>
int main()
{
SortableVector<std::string> sv;
sv.data = {"one","two","three","four"};
sv.sort();
for(auto& s : sv.data)
std::cout << s << ' ';
}
DISCLAIMER This seems a very non-c++ way to design classes. Algorithms and containers are traditionally separated, for good reason. The exception being that member functions sometimes perform operations in more optimal ways (e.g. std::set<>::find instead of std::find)
It should be std::sort(aptr, aptr + arraySize);. Also you should check arraySize > 0 before doing this, if you're intending to catch errors like that instead of having undefined behaviour.
begin and end aren't magic; they rely on the object either being an array (not a pointer), or the object implementing functions .begin() and .end() that return iterators.
You could even define those functions yourself, e.g. SimpleVector<T>::begin() { return aptr; } etc. , but maybe this is not a good idea as it bypasses your bounds-checking. You'd have to actually write an iterator class instead of returning a pointer, which is more trouble than you're ready for :)
NB. Everywhere you write this->operator[](foo), it would be clearer to write (*this)[foo]