subclassing with operator overloading c++ - c++

I am not the most experienced C++ programmer and new to this forum.
I hope this first post follows the rules.
My goal is to write a simple raytracer, but I am stuck on how to extend one of my classes.
The problem lies with operator overloading, but also design-patterns (I think). Here is my code:
class Vec{ //My vector class
protected:
double v[4]; //Fourth element not used...
public:
Vec();
Vec (double i, double j, double k);
Vec operator+ (const Vec& vec) const;
Vec operator- (const Vec& vec) const;
Vec & operator+=(const Vec& vec);
Vec & operator-=(const Vec& vec);
double operator*(const Vec& vec) const;
Vec operator*(double scalar);
//get operator
double& operator[](unsigned int i);
double operator[](unsigned int i) const;
//Vector assignment operator
Vec Vec::operator=(const Vec& vec);
//Scalar assignment operator
Vec Vec::operator=(double scalar);
};
Vec Vec::operator+(const Vec& vec) const{
return Vec(v[0]+vec.v[0], v[1]+vec.v[1], v[2]+vec.v[2]);
}
And
class Color : public Vec{
protected:
Vec col; //A Color should use all four elements in the array
//I.e Vec color(R,G,B,specular);
public:
//Vec Color::operator+(const Vec& vec) const; <-- this is wrong i know.
};
Just to be thorough, I do this in main:
Color myColor(1.2, 1.3, 2.3, 40);
Color myColor2(2.2, 3.4, 3.3, 30);
cout << (myColor+myColor2) << std::endl;
The program calls the Vec() default constructor returning an output of (0,0,0) which is clearly wrong....
Edit: The Vec class stores values like this
Vec::Vec () {
v[0] = 0; v[1] = 0; v[2] = 0;
}
Vec::Vec (double i, double j, double k) {
v[0] = i; v[1] = j; v[2] = k;
}
All overloaded operators work on double v[4] and return a Vec either a copy or reference.
Question: What is the most efficient/proper way to make Color utilize the operators from Vec? Would making the overloaded operators into template functions solve my problem (how?). Or should I take an entirerly different approach? I look forward to any help I can get, thank you.

Why not just something like this:
#include <cstddef>
template <size_t N>
class Vector {
public:
Vector();
friend Vector operator+(const Vector& lh, const Vector& rh);
//...
private:
double val[N];
};
typedef Vector<3> Vector3d;
typedef Vector<4> Color;

Related

c++ 2 overloads have similar conversions depending on whether the operator is a member function or a global one

Consider a simple vector class realization:
#include <algorithm>
class Vector {
public:
Vector(int _elementsCount)
: elementsCount(_elementsCount)
, elements(new float[_elementsCount])
{}
~Vector() {
delete[] elements;
}
Vector(const Vector& rhs) {
elementsCount = rhs.size();
elements = new float[elementsCount];
for (int i = 0; i < elementsCount; ++i)
(*this)[i] = rhs[i];
}
float& operator [](int i) {
return elements[i];
}
float operator [](int i) const {
return const_cast<Vector&>(*this)[i];
}
int size() const {
return elementsCount;
}
/*// Dot product
float operator *(const Vector& v) {
float res = 0;
for (int i = 0; i < size(); ++i)
res += (*this)[i] * v[i];
return res;
}*/
private:
int elementsCount;
float* elements;
};
// Multiplication by a scalar
Vector operator *(const Vector& v, float k) {
Vector res(v.size());
for (int i = 0; i < v.size(); ++i)
res[i] = v[i] * k;
return res;
}
// Dot product
float operator *(const Vector& v1, const Vector& v2) {
float res = 0;
for (int i = 0; i < std::min(v1.size(), v2.size()); ++i)
res += v1[i] * v2[i];
return res;
}
void main()
{
Vector v(2);
v * 3; // ambiguous
}
This code compiles. But if we uncomment * operator realization in the class and comment its global realization (dot product function), then there will be an error "'Vector::operator *': 2 overloads have similar conversions", because there is an ambiguity: whether to call the multiplication by a scalar or to interpret 3 as an argument to a parametrized constructor and to call the dot product. This makes sense. But I don't get what's the difference of declaring the * operator as a member function or as a global function. I thought they should be the same in the example like above, but it's not the case.
Added. The thing I most interested in is not how to avoid the ambiguity, but why there is an ambiguity in one case (when * is declared as a member) and there is no one in the other (when * is declared as a global function).
You need to make your constructor explicit:
explicit Vector(int _elementsCount) { ... }
The reason for the ambiguity is that the compiler can't decide whether it should implicitly convert a int value to a Vector and invoke Vector::operator*, or implicitly convert a int value to a float and use operator*(const Vector&, float).
By using explicit, such conversions are forbidden, and you must use Vector(3) if you want "3" to be a Vector.
As a side-note, you should make the operator const, since it does not modify the object. Making it const will also allow it to be used with a const Vector:
float operator *(const Vector& v) const { ... }
Beware that will still conflict with your other overload:
float operator *(const Vector& v1, const Vector& v2)
There is no reason to have both. Choose either the member function or the global function and remove the other.

How to overload subscript operator [] to reference 2d STL array?

I am trying to create an overloaded operator for my class, my class contains a 2d array container. I want use the operator as follows: fooClass a; a[i][j] = 4;
i don't want to use it like a[i, j], a(i, j) or call a function to input or output an element. Is is possible?
Chaining the call wont work since the first returns pointer to array, and the second should return an element in an array i.e. a float.
my class looks something like this:
class foo
{
public:
foo();
~foo();
foo& operator=(const foo&rhs);
foo& operator=(const foo&&rhs);
foo& operator=(std::initializer_list<std::initializer_list<float>> il);
friend std::ostream& operator<<( std::ostream& os, const foo&rhs);
std::array<float, 3>& operator[](const int & rhs); <<<<HERE<<<< What should it return?
private:
std::array<std::array<float, 3>, 3> matrix;
};
int main()
{
foo a;
a = {{1,2,3},{4,5,6},{7,8,9}};
a[1][2] = 13;
cout << a[1][2] << endl;
return(0);
}
My question is, how to do this, and what should the function ..... operator[](const int & rhs); return?
FYI I am not using the array container directly because I am implementing other functions also I am doing the matrix column major.
Doesn't this work?
std::array<float, 3>& operator[](const int & rhs) {
return matrix[rhs];
}

How to overload operators with Complex Numbers and Polymorphism?

I have to create a Complex class and a vector class that includes vectors of Complex numbers. I have to overload +,-,/,*, << in both classes. Would they both be member functions or would it be easier to put them as nonmember functions? In addition, is there a way to make the vector class only have the vector as a parameter, rather than including the real and imaginary number and vector? The following code from is my class member functions of the vector class.
class ComplexVector : public Complex //vectors whose elements are complex numbers
{
public:
ComplexVector();
ComplexVector(double real, double imaginary, std::vector <Complex> vec);
ComplexVector(std::vector <Complex> vec);
friend ComplexVector operator+ (const ComplexVector& v1, const ComplexVector& v2);
friend ComplexVector operator- (const ComplexVector& v1, const ComplexVector& v2);
friend ComplexVector operator* (const ComplexVector& v1, const ComplexVector& v2);
friend ComplexVector operator/ (const ComplexVector& v1, const ComplexVector& v2);
int get_size() const;
private:
std::vector <Complex> cvec;
};
std::ostream& operator<< (std::ostream& stream, const ComplexVector& comvec);
I put the explanations of each function in a separate cpp file. Even though I used friend in the header file, on the cpp file it says that i am not allowed to use two parameters. Is there a way to get out of that? In addition, would I have to use pointers in order to access what is in the vector? I have heard of using vector but I have tried and the arithmetic doesn't make sense to me. (Note: I was playing around so not all the code is exactly correct) Thanks!
ComplexVector::ComplexVector() : Complex() {}
ComplexVector::ComplexVector(double real, double imaginary, std::vector<Complex> vec) : Complex(real, imaginary), cvec(vec) {}
ComplexVector::ComplexVector(std::vector<Complex>vec) {cvec = vec;}
ComplexVector& ComplexVector::operator+ (ComplexVector v1, const ComplexVector& v2)
{
std::vector <Complex> add;
for (int i = 0; i < v1.get_size(); i++)
{
double xside = v1[i].get_real() + v2[i].get_real();
double yside = v1[i].get_()imaginary + v2[i].get_imaginary();
add[i] = Complex(xside,yside);
}
ComplexVector add_sol(add);
return add_sol;
}
ComplexVector ComplexVector::operator- (const ComplexVector& v1, const ComplexVector& v2)
{
std::vector <Complex> sub;
for (int i= 0; i < v1.get_size(); i++)
{
sub[i] = v1[i] - v2[i]; //same as +?
//does not provide subscript operator????
}
ComplexVector sub_sol(sub);
return sub_sol;
}
ComplexVector ComplexVector::operator* (const ComplexVector& v2)
{
ComplexVector multi;
for (int i = 0; i < v1.size(); i++)
{
multi[i] = v1[i] * v2[i];
}
return multi;
}
ComplexVector operator/ (const ComplexVector& v2)
{
ComplexVector div;
for (int i = 0; i < v1.size(); i++)
{
div[i] = v1[i] / v2[i];
}
return div;
}
int ComplexVector::get_size() const
{
int total;
for (int i = 0, i < cvec.size(); i++)
{
total++;
}
return total;
}
std::ostream& ComplexVector::operator<< (std::ostream& stream, const ComplexVector& comvec)
{
for (int i = 0; i < comvec.size(); i++)
{
stream << comvec[i] -> get_real() << " " << "+" << " " << comvec[i] -> get_imaginary() << "i" ;
}
return stream;
}
Operators defined in the class:
If a class declares the operator + as follow:
class A {
public:
A operator+(A b);
};
It means that the b is the second operand, the first being the object on which the operator is invoked. Otherwise said, with x, y and z being of class A:
z = x + y;
is equivalent to:
z = x.operator+(y);
Unfortunately the ComplexVector:: in the following definition indicates that you are defining the operator as class member:
ComplexVector& ComplexVector::operator+ (ComplexVector v1, const ComplexVector& v2)
The thing is that operator+ takes two operands. In this case it'll be the object itself and v1. So the compiler complains about the second argument that is explicitly declared, which would be the third operand of operand+. which is illegal.
Friend operators defined outside the class
If you define a friend operator like:
class A {
public:
friend A operator-(A a, A b);
};
it means that the operator- is not a class member but an independent operator. As such it needs two arguments for an operation to occur. So
z = a - b;
would be equivalent to
z = ::operator- (a, b);
Unfortunately you do not use that approach in your code.
Other remarks
You need to declare the operators according to some general rules, so that they keep a semantic of the natural operators.
As such for +, -, * and / you should not take arguments by reference (or eventually a const reference) and you should always return by value, including for operator+.
Here a step by step article by Herb Sutter to explain more in details some constraints around operator overloading. Here a more advanced guideline.
Finally, without wanting to be rude, what's the logic of making a vector of complex inherit from a complex ? A vector of complex is not a kind of complex. It's a completely different class, with a dependency relationship, but there's absolutely no justifiable reason for the inheritance ! Or is there ?

Operator overloading as friend function error

I'm trying to use + to add 2 vector (mathematical vector). Here's my code:
class Vector{
double v[Max_size];
int dim;
public:
int getDim() const;
Vector();
Vector(int n);
Vector(const Vector& a);
Vector add(const Vector&b);
friend Vector operator+(Vector summand1, Vector summand2);
};
Operator overloading:
Vector operator+(Vector summand1, Vector summand2){
int dim1 = summand1.getDim();
int dim2 = summand2.getDim();
assert(dim1 == dim2);
Vector sum(dim1);
int i;
for(i = 0; i < dim1; i++){
sum.v[i] = summand1.v[i] + summand2.v[i];
}
return sum;
}
And how I use it:
Vector m = v+t;
When I run the code, it always shows that m is (0,0) (2D vector), which is the default value generated by the constructor. What's wrong with it? Thanks!
Your copy constructor:
Vector::Vector(const Vector& a){
dim = a.dim;
Vector(dim);
}
correctly sets the value of the dim member, but has not other side effect.
You should have a variant of the following code:
Vector::Vector(const Vector& a) : dim(a.dim) {
std::copy(std::begin(a.v), std::end(a.v), v);
}
This will actually copy the data present in the parameter, and you will see the correct behavior for the code:
// Copy constructor called here, but did not correctly copy the data before.
Vector m = v + t;
For a better (by that I intend simpler and safer) Vector class, if you have access to a compiler that is at least C++11 compliant, you can write:
class Vector{
std::array<double, Max_size> v; // Note the std::array here.
int dim;
public:
int getDim() const;
Vector();
Vector(int n);
Vector(const Vector& a);
Vector add(const Vector&b);
friend Vector operator+(Vector summand1, Vector summand2);
};
The std::array will take care of everything, provided you write your copy constructor like this:
Vector::Vector(const Vector& a) : v(a.v), dim(a.dim) {
}
Or, even better, you could then let the compiler generate the copy constructor itself, with the same behavior.

Can't assign a value to object of my custom class, that shall behave like a matrix

I have custom class, that behaves like matrix. Everything works well, except assigning a value from other instance of the same class.
So I can do stuff like:
Matrix a(5,7);
// du stuff with a
Matrix b(5,7);
Matrix d=a+b;
d=a*5;
d[3][2]=1;
//but I can't do this:
double x=d[3][2];
//at this point I get this error:
main.cpp:604:12: error: passing ‘const Matrix’ as ‘this’ argument of ‘Matrix::Proxy Matrix::operator[](int)’ discards qualifiers
Does anybody have any idea, how to fix this? :(
Implementation of my matrix class is here:
class Matrix {
public:
Matrix(int x, int y);
~Matrix(void);
//overloaded operators
Matrix operator+(const Matrix &matrix) const;
Matrix operator-() const;
Matrix operator-(const Matrix &matrix) const;
Matrix operator*(const double x) const;
Matrix operator*(const Matrix &matrix) const;
friend istream& operator>>(istream &in, Matrix& a);
class Proxy {
Matrix& _a;
int _i;
public:
Proxy(Matrix& a, int i) : _a(a), _i(i) {
}
double& operator[](int j) {
return _a._arrayofarrays[_i][j];
}
};
Proxy operator[](int i) {
return Proxy(*this, i);
}
// copy constructor
Matrix(const Matrix& other) : _arrayofarrays() {
_arrayofarrays = new double*[other.x ];
for (int i = 0; i != other.x; i++)
_arrayofarrays[i] = new double[other.y];
for (int i = 0; i != other.x; i++)
for (int j = 0; j != other.y; j++)
_arrayofarrays[i][j] = other._arrayofarrays[i][j];
x = other.x;
y = other.y;
}
int x, y;
double** _arrayofarrays;
};
You currently have one operator[] signature:
Proxy operator[](Matrix *this, int i)
You're trying to call this:
Proxy operator[](const Matrix *, int)
The error is saying that in order to convert from const Matrix * to Matrix *, the const has to be discarded, which is bad. You should provide a const version inside your class:
Proxy operator[](int) const {...}
When inside your class, it gains a first parameter of this, and the const after the parameter list means that the first parameter will be a pointer to a constant object of your class, not a non-constant one.