- #36
Vanadium 50
Staff Emeritus
Science Advisor
Education Advisor
2023 Award
- 33,427
- 19,973
Filip Larsen said:Hint:
I see you tried this once too.
Filip Larsen said:Hint:
ThanksFilip Larsen said:Line 9 (of your commented code block): the constructor notation: x(x), y(y), z(z)
is member initialization, so the member variable x is initialized with the formal constructor parameter x. This means the matched member constructor is called. If you do as on (your) line 10 then the members are first default constructed (which for doubles means not initialized) and then they are assigned a value. If you are confused about the repeated name, then note constructor in line 9 could also be written as, say,vec3(double xi = 0, double yi = 0, double zi = 0) : x(xi), y(yi), z(zi) {}
.
Line 21 defines three vec3 instances, r0, v0 and a, and uses constructor arguments to initialize their x, y, z member with values. If the construct arguments had been left out the coordinates would have been initialized to 0 due to the default values in the constructor. The values themselves are written as integers, but C++ will implicit convert these values to doubles when matching which constructor to use.
Line 23: Not sure what your question is, but r is defined and initialized with the result of the calculation.
No I am not confused, my first read (very late last night) was thinking and actually asked to confirm whether r0, v0 and a are integers to multiply into the initial values given into line 21. And no, it's not that I don't understand operator+(). I never learn using this as vector addition before. I just BARELY learn this about a week or so ago, with one example in the book that it's not even worth putting in the notes. The only one that mean something is Jtbell's example which is a whole hell of a lot simpler than this. That's why I kept wanting to venture out to do extra rather just follow the book. It's very discouraging to hear this from you.Mark44 said:You're missing a couple of important points here.
First, the work of writing an overloaded operator happens once, but once done, it can be used many times. When you overload an operator, you are in essence extending the language, allowing it do some operation that isn't part of the language definition.
Second, the overloaded operator doesn't just make things look better - they allow code that uses the operator to be more natural, with the potential benefit of code that is less prone to errors. For example, if AddrBkA and AddBkB are directories, having an overloaded operator+() could allow a user to write AddrBkA + AddrBkB as an expression that would merge two address books.
Another point that you're missing is that their are hierarchies of programmers. At the lowest level are programmers who use existing libraries to create applications. At a higher level are those who create libraries and classes that others can use. In your case you have been wearing both hats, so the distinction isn't clear: the application developer who writes main(), and the class designer/implementer.
Not at all. And you have seen numerous examples in this thread.
No, this is not so. Already pointed out - the overloaded operator+() in the example allows the program to calculate ##\vec v = \vec a + \vec b##. What is returned is a vector. You apparently missed that the result object r was declared to be of type Vec3.
For the example shown it would also have been convenient to overload the * operator, to allow multiplication of a Vec3 object by a scalar. (In the assignment statement above, t and 0.5 are scalars, floating point numbers in this case.)
Here's what you wrote when asking about the code that Filip Larsen wrote:yungman said:No I am not confused, my first read (very late last night) was thinking and actually asked to confirm whether r0, v0 and a are integers to multiply into the initial values given into line 21.
It should have been obvious that r0, v0, and a are all objects of the vec3 class, and that all three of these objects are being created and initialized as shown by the argument lists.yungman said:C++:int main() { // are r0, v0,a are numbers? what? integer or float? vec3 r0(1, 2, 3), v0(2, 3, 4), a(1, 1, 1); //declare 3 object of vec3?
I don't know what you mean by "I never learn using this as vector addition before."yungman said:And no, it's not that I don't understand operator+(). I never learn using this as vector addition before.
vec3 operator*(double k, const vec3& rhs) { return rhs * k; }
vec3 operator*(double k) const { return { k * x, k * y, k * z }; }
I assume you mean his program in your other thread about operator overloads. This is one of the lines in his program:yungman said:I was looking at Jtbell's program as class object with 3 arguments, never think of it as vector.
ThreeVector a(0, 0, -10), r0(0, 0, 0), v0(70, 0, 70);
Some of them get it right away, and some don't. I always encourage them to ask questions, but I also encourage them to do the reading as well.yungman said:You are a professor, you really think your student can really get all these when you first teach them? They might be quiet and not asking question, you really think they actually understand and remember all that right away?
I never learn line 13, I only learn operator*() of line 10 with single parameter. My question is what is line 13?Filip Larsen said:You have seen at least one example before . Let me make a fresh small example:
C++:// C++ 17 #include <iostream> using namespace std; struct vec3 { double x, y, z; vec3(double x = 0, double y = 0, double z = 0) : x(x), y(y), z(z) {} vec3 operator+(const vec3& rhs) const { return { x+rhs.x, y+rhs.y, z+rhs.z }; } vec3 operator*(double k) const { return { k*x, k*y, k*z }; } }; vec3 operator*(double k, const vec3& rhs) { return rhs*k; } ostream& operator<<(ostream& os, const vec3& v) { return os << "(" << v.x << " " << v.y << " " << v.z << ")"; }int main() { vec3 r0(1, 2, 3), v0(2, 3, 4), a(1, 1, 1); double t = 2.0; vec3 r = r0 + t*v0 + 0.5 * a * t * t; cout << "r = " << r << endl; return 0; }
yungman said:My question is what is line 13?
this
. This means the compiler will parse expressions like "(vec3 instance expression) * (double value expression)" using this operator overload.std::ostream
reference.class vec3 {
public:
vec3 multiply(const vec3& rhs);
};
vec3 operator*(const vec3& lhs, const double rhs) { return lhs.multiply(rhs); }
vec3 operator*(const double lhs, const vec3& rhs) { return rhs.multiply(lhs); }
ostream& operator<<(ostream& os, const vec3& v) { return os << "(" << v.x << " " << v.y << " " << v.z << ")"; }
inside the class.#include <iostream>
#include <cstring>
using namespace std;
const int Nsize = 51;
char Add[Nsize] = "temp0", Multi1[Nsize] = "mul10", Multi2[Nsize] = "mul20";
class vec3 {
private: double x, y, z; char name[Nsize];
public:
vec3(const char *desc, double x = 0, double y = 0, double z = 0) : x(x), y(y), z(z)
{strncpy_s(name, Nsize, desc, Nsize);
cout << "[Con] for " << (*this).name << "(" << this->x <<
", " << this->y << ", " << this->z << ")\n\n";
}
/* vec3(vec3& obj)
{ strncpy_s(name, Nsize, "Cpycon", Nsize);
this->x = obj.x; this->y = obj.y; this->z = obj.z;
cout << "[CopyC]" << name << "(" << x << ", " << y << ", " << z <<
" copied from" << obj.name<<"(" << obj.x << ", " << obj.y << ", " << obj.z << ")\n\n";
}*/
vec3 operator+(const vec3& rhs)
{ vec3 sum(name, 0, 0, 0);
strncpy_s(sum.name, Nsize, Add, Nsize);
Add[4]++;
sum.x = x + rhs.x; sum.y = y + rhs.y; sum.z = z + rhs.z;
cout << " [Op+] for " << this->name << "(" << this->x <<
", " << this->y << ", " << this->z << ")\n\n";
return (sum ); }
vec3 operator*(double k) const
{ vec3 mul1(name, 0, 0, 0);
strncpy_s(mul1.name, Nsize, Multi1, Nsize);
Multi1[4]++;
mul1.x = k * x; mul1.y = k * y; mul1.z = k * z;
cout << " [Op1*] for " << this->name << "(" << this->x <<
", " << this->y << ", " << this->z << ")\n\n";
return mul1; }
};
/*vec3 operator*(double k, const vec3& rhs)
{ return rhs * k; }
vec3 operator*(double k, const vec3& rhs)
{ vec3 mul2(char name[Nsize], 0, 0, 0);
strncpy_s(mul2.name, Nsize, Multi2, Nsize);
Multi1[4]++;
mul2.x = k * x; mul2.y = k * y; mul2.z = k * z;
cout << " [Op1*] for " << this->name << "(" << this->x <<
", " << this->y << ", " << this->z << ")\n\n";
return mul2; }*/
/*ostream& operator<<(ostream& os, const vec3& v)
{ return os << "(" << v.name << v.x << " " << v.y << " " << v.z << ")"; }*/int main()
{ double t = 2.0;
vec3 r0("r0", 1, 2, 3),
v0("v0", 2, 3, 4),
a("a", 1, 1, 1);
vec3 r = r0 +
v0*t +
a * ( t * t * 0.5)
;
//cout << "r = " << r << endl;
return 0;
}
yungman said:But I think you can useostream& operator<<(ostream& os, const vec3& v) { return os << "(" << v.x << " " << v.y << " " << v.z << ")"; }
inside the class.
Can you tell me what is the name of the function type in line 13, I want to read up more on overloading function that can only be outside of the class.
https://en.cppreference.com/w/cpp/language/operators said:Binary operators are typically implemented as non-members to maintain symmetry (for example, when adding a complex number and an integer, if operator+ is a member function of the complex type, then only complex+integer would compile, and not integer+complex). Since for every binary arithmetic operator there exists a corresponding compound assignment operator, canonical forms of binary operators are implemented in terms of their compound assignments:
class X
{
public:
X& operator+=(const X& rhs) // compound assignment (does not need to be a member,
{ // but often is, to modify the private members)
/* addition of rhs to *this takes place here */
return *this; // return the result by reference
}
// friends defined inside class body are inline and are hidden from non-ADL lookup
friend X operator+(X lhs, // passing lhs by value helps optimize chained a+b+c
const X& rhs) // otherwise, both parameters may be const references
{
lhs += rhs; // reuse compound assignment
return lhs; // return the result by value (uses move constructor)
}
};
#include <iostream>
#include <cstring>
using namespace std;
const int Nsize = 51;
char Add[Nsize] = "temp0", Multi1[Nsize] = "mul10", Multi2[Nsize] = "mul20";
class vec3 {
private: char name[Nsize]; double x, y, z;
public:
vec3(const char *desc, double x = 0, double y = 0, double z = 0) : x(x), y(y), z(z)
{strncpy_s(name, Nsize, desc, Nsize);
cout << "[Con] for " << (*this).name << "(" << this->x <<
", " << this->y << ", " << this->z << ")\n\n";
}
/* vec3(vec3& obj)
{ strncpy_s(name, Nsize, "Cpycon", Nsize);
this->x = obj.x; this->y = obj.y; this->z = obj.z;
cout << "[CopyC]" << name << "(" << x << ", " << y << ", " << z <<
" copied from" << obj.name<<"(" << obj.x << ", " << obj.y << ", " << obj.z << ")\n\n";
}*/
vec3 operator+(const vec3& rhs)
{ vec3 sum(name, 0, 0, 0);
strncpy_s(sum.name, Nsize, Add, Nsize);
Add[4]++;
sum.x = x + rhs.x; sum.y = y + rhs.y; sum.z = z + rhs.z;
cout << " [Op+] for " << this->name << "(" << this->x <<
", " << this->y << ", " << this->z << ")\n\n";
return (sum ); }
vec3 operator*(double k) const
{ vec3 mul1(name, 0, 0, 0);
strncpy_s(mul1.name, Nsize, Multi1, Nsize);
Multi1[4]++;
mul1.x = k * x; mul1.y = k * y; mul1.z = k * z;
cout << " [Op1*] for " << this->name << "(" << this->x <<
", " << this->y << ", " << this->z << ")\n\n";
return mul1; }
// friend vec3 operator*(double k, const vec3& rhs)
//{ return rhs * k; }
friend vec3 operator*(double k, const vec3& rhs)
{ vec3 mul2(name, 0, 0, 0);
strncpy_s(mul2.name, Nsize, Multi2, Nsize);
Multi2[4]++;
mul2.x = k * rhs.x; mul2.y = k * rhs.y; mul2.z = k * rhs.z;
cout << " [Op1*] for " << mul2.name << "(" << mul2.x <<
", " << mul2.y << ", " << mul2.z << ")\n\n";
return mul2; }
friend ostream& operator<<(ostream& os, const vec3& v)
{ return os << "(" << v.name << v.x << " " << v.y << " " << v.z << ")"; }
};
#include <iostream>
#include <cstring>
using namespace std;
const int Nsize = 51;
char Add[Nsize] = "temp0", Multi1[Nsize] = "mul10", Multi2[Nsize] = "mul20";
class vec3 {
private:
public:
char name[Nsize]; double x, y, z;
vec3(const char *desc, double x = 0, double y = 0, double z = 0) : x(x), y(y), z(z)
{strncpy_s(name, Nsize, desc, Nsize);
cout << "[Con] for " << (*this).name << "(" << this->x <<
", " << this->y << ", " << this->z << ")\n\n";
}
vec3(const vec3& obj)
{ strncpy_s(name, Nsize, "Cpycon", Nsize);
x = obj.x; y = obj.y; z = obj.z;
cout << "[CopyC]" << name << "(" << x << ", " << y << ", " << z << ") copied from "
<< obj.name<<"(" << obj.x << ", " << obj.y << ", " << obj.z << ")\n\n";
}
vec3 operator+(const vec3& rhs)
{ vec3 sum(name, 0, 0, 0);
strncpy_s(sum.name, Nsize, Add, Nsize);
Add[4]++;
sum.x = x + rhs.x; sum.y = y + rhs.y; sum.z = z + rhs.z;
cout << " [Op+] for " << this->name << "(" << this->x <<
", " << this->y << ", " << this->z << ")\n\n";
return (sum ); }
vec3 operator*(double k) const
{ vec3 mul1(name, 0, 0, 0);
strncpy_s(mul1.name, Nsize, Multi1, Nsize);
Multi1[4]++;
mul1.x = k * x; mul1.y = k * y; mul1.z = k * z;
cout << " [Op1*] for " << this->name << "(" << this->x <<
", " << this->y << ", " << this->z << ")\n\n";
return mul1; }
// friend vec3 operator*(double k, const vec3& rhs)
//{ return rhs * k; }
friend vec3 operator*(const double k, const vec3& rhs)
{ vec3 mul2("mul2", 0, 0, 0);
strncpy_s(mul2.name, Nsize, Multi2, Nsize);
Multi2[4]++;
mul2.x = k * rhs.x; mul2.y = k * rhs.y; mul2.z = k * rhs.z;
cout << " [Op1*] for " << mul2.name << "(" << mul2.x <<
", " << mul2.y << ", " << mul2.z << ")\n\n";
return mul2; }
friend ostream& operator<<(ostream& os, const vec3& v)
{ return os << " " << v.name <<"(" << v.x << " " << v.y << " " << v.z << ")"; }
};
int main()
{ double t = 2.0;
vec3 r0("r0", 1, 2, 3),
v0("v0", 2, 3, 4),
a("a", 1, 1, 1);
vec3 r = r0 +
t*v0 +
( t * t * 0.5)* a
;
cout << "r = " << r << endl;
return 0;
}
}
yungman said:I am really confused, I changed line 40 from name to "mul2", it works. I really don't understand. Why line 30 works with name, but the friend operator will not work?
this
defined. Defining a friend function textually inside a class definition does no in itself mean its a member function; it means just the same as if the friend function was defined outside the lexical scope of the class. Also, contrast this with the member function you define in line 30 which do have a this
defined, so name
there implicitly refers to this->name
.rhs.name
in line 40, or, since you overwrite mul2.name
with Multi2
on the next line anyway, just use Multi2
in line 40 and skip the string copy. But if you get into more trouble fumbling around with those damn c-string you know what I am going to say .ThanksFilip Larsen said:You are getting the error in line 40 because that friend function is not a member function, but more like a static function, and for those there are nothis
defined. Defining a friend function textually inside a class definition does no in itself mean its a member function; it means just the same as if the friend function was defined outside the lexical scope of the class. Also, contrast this with the member function you define in line 30 which do have athis
defined, soname
there implicitly refers tothis->name
.
As a minimal fix closest to your original intent I will suggest you just userhs.name
in line 40, or, since you overwritemul2.name
withMulti2
on the next line anyway, just useMulti2
in line 40 and skip the string copy. But if you get into more trouble fumbling around with those damn c-string you know what I am going to say .
#include <iostream>
using namespace std;
class Complex
{ public:
double r, i;
Complex(double r = 0, double i = 0):r(r), i(i) {};
Complex operator+(const Complex& rhs)
{ Complex sum;
sum.r = this->r + rhs.r; sum.i = this->i + rhs.i;
return sum;
}
friend Complex operator*(const Complex& left, const Complex& right)
{ Complex mul;
mul.r = left.r * right.r - left.i * right.i;
mul.i = left.r * right.i + left.i * right.r;
return mul;
}
void result(const Complex& obj)//Correct display format depends on sign of imaginary part.
{ if (obj.i >= 0){ cout << " (" << obj.r << " + " << obj.i << "j)\n\n";}
else { cout << " ("<<obj.r << " - " << abs(obj.i) << "j)\n\n"; }
}
};
int main()
{ Complex A, B, C;
cout << " Enter the real part of A: "; cin >> A.r;
cout << " Enter the imaginary part of A: "; cin >> A.i;
cout << " Enter the real part of B: "; cin >>B.r;
cout << " Enter the imaginary part of B: "; cin >> B.i; cout << endl;
C = A * B;
cout << " A X B ="; C.result(C);
C = A + B;
cout << " A + B ="; C.result(C);
return 0;
}
Filip Larsen said:You are getting the error in line 40 because that friend function is not a member function, but more like a static function, and for those there are nothis
defined. Defining a friend function textually inside a class definition does no in itself mean its a member function; it means just the same as if the friend function was defined outside the lexical scope of the class. Also, contrast this with the member function you define in line 30 which do have athis
defined, soname
there implicitly refers tothis->name
.
As a minimal fix closest to your original intent I will suggest you just userhs.name
in line 40, or, since you overwritemul2.name
withMulti2
on the next line anyway, just useMulti2
in line 40 and skip the string copy. But if you get into more trouble fumbling around with those damn c-string you know what I am going to say .
yungman said:BUT I declare mul2 as object of Complex. Does it matter whether it's a member function or only a friend?
this
). The fact that it in this case is declared in the lexical scope of the class does not change anything. Consider the example#include <iostream>
using namespace std;
class Thing {
int m_a;
friend void f(Thing& t) {
t.m_a = 3;
}
public:
int a() const { return m_a; }
};
int main()
{
Thing t;
f(t);
cout << "t.a = " << t.a();
return 0;
}
#include <iostream>
using namespace std;
class Thing {
int m_a;
friend void f(Thing& t);
public:
int a() const { return m_a; }
};
void f(Thing& t) {
t.m_a = 3;
}
int main() {
Thing t;
f(t);
cout << "t.a = " << t.a();
return 0;
}
Complex
class you have defined Complex operator*(const Complex& lhs, const Complex& rhs)
, but since the first parameter is a Complex
the normal practice is to define it as the overloaded member function Complex operator*(const Complex& rhs) const
where left hand side inside the implementation of that member function is represented by this
. The friend operator*
notation/idiom is only required when the left-hand argument is NOT of the type in question, e.g. in your case if you want to have an operator so you can write (double expression)*(Complex expression)
.yungman said:I should be able to use the public member name[Nsize] for mul2. I still don't quite get what the reason.
mul2.name
.Thanks so much Filip, I am going to read through chapter 13 and 14 on classes. Old brain needs to refresh the memory concentrating on member data access and all that. It's so easy to just follow the syntax and do things without really understanding them, that's why I want to keep writing stupid codes like those to keep going through them to see anything slipped through the cracks.Filip Larsen said:(I assume you still refer to your vec3 class in post #48 and not your new Complex class from post #50.)
As I replied in post #49, the friend operator overload is really considered to be just a function and not a member function with an implied object context (i.e.this
). The fact that it in this case is declared in the lexical scope of the class does not change anything. Consider the example
this is really understood by the compiler asC++:#include <iostream> using namespace std; class Thing { int m_a; friend void f(Thing& t) { t.m_a = 3; } public: int a() const { return m_a; } }; int main() { Thing t; f(t); cout << "t.a = " << t.a(); return 0; }
That is, the function f is considered to "be outside" the Thing class even though it (in first example above) is define lexically inside the Thing class.C++:#include <iostream> using namespace std; class Thing { int m_a; friend void f(Thing& t); public: int a() const { return m_a; } }; void f(Thing& t) { t.m_a = 3; } int main() { Thing t; f(t); cout << "t.a = " << t.a(); return 0; }
Also, I can see in your newComplex
class you have definedComplex operator*(const Complex& lhs, const Complex& rhs)
, but since the first parameter is aComplex
the normal practice is to define it as the overloaded member functionComplex operator*(const Complex& rhs) const
where left hand side inside the implementation of that member function is represented bythis
. Thefriend operator*
notation/idiom is only required when the left-hand argument is NOT of the type in question, e.g. in your case if you want to have an operator so you can write(double expression)*(Complex expression)
.
In the context of your original question relating to this, yes, you can refer to it asmul2.name
.