A struct deflauts to public access to its members, while a class defaults to private access.
explicit Point(float X=.0, float Y=.0, float Z=.0) : x(X), y(Y), z(Z) { } explicit Point(float* ptr) : x(ptr[0]), y(ptr[1]), z(ptr[2]) { }
Point pnt1; // call first constructor with all default arguments. Point pnt2(1.0); // call first constructor with all arguments 1.0, 0.0, and 0.0. float val[3] = {1, 2, 3}; Point pnt3(val); // call 2nd constructor. Point pnt4(pnt1); // call (compiler-generated) copy constructor. copy data members from pnt1.
It literally means you have to construct the object explicitly.
If without explicit modifier, the following is valid code,
Point pnt; ... pnt = 5.0;
With explicit, the above code has to be changed into
Point pnt; ... pnt = Point(5.0);
Compilers usually implement them the same way. The difference is C++ references are more programmer friendly.
Point p; Point p_ref = p; Point p_ptr = &p; p_ref.GetNorm(); // the same as p.GetNorm(); p_ptr->GetNorm(); // the same as p.GetNorm();
Another difference is that a reference has to be initialized to refer to some actual object, while a pointer does not.
void a_func1(Point const& p) { ... } void a_func2(Point const* p=0) { if(p) { ... } else { ... }; }
Same function name, different arguments. The compiler resolves the name conflict by looking at the given arguments.
The constructors are functions too. All class constructor are overloaded. If the user does not explicitly define any constructor, the compiler will generate two,
Aclass() { /* defautl constructor. initialize all member data with their default constructors. */ } Aclass(const Aclass&) { /* copy constructor. copy all members from the given object. */ }
Open GL is written in C language, and cannot take advantage of function overloading.
glVertex2f(1,0); glVertex3f(1,0, 1);
Point p(3,4,0); float len; len = p.GetNorm(); len = (&p)->GetNorm();
2 + 3; // this expression is logically equivalent to function call: operator+(2, 3). 1, 2, 3, 4; // this expression is logically equivalent to function call: operator,(1,2,3,4).
Think about this code (a common error for C++ beginners),
Point p = (100.0, 10.0, 100.0);
All the following member functions do not change the object, and are therefore defined as const.
Point GetUnit() const { return Point(x/GetNorm(), y/GetNorm(), z/GetNorm()); } float GetNorm() const { return sqrt(x*x + y*y + z*z); } Point operator-() const { return Point(-x, -y, -z); } Point operator+(Point const& rhs) const { Point tmp(*this); tmp += rhs; return tmp; } Point operator-(Point const& rhs) const { Point tmp(*this); tmp -= rhs; return tmp; } Point operator*(float scale) const { Point tmp(*this); tmp *= scale; return tmp; } Point operator/(float scale) const { Point tmp(*this); tmp /= scale; return tmp; } bool operator==(Point const& rhs) const { return x==rhs.x && y==rhs.y && z==rhs.z; } bool operator!=(Point const& rhs) const { return ! ( (*this) == rhs ); } float operator*(Point const& rhs) const { return x * rhs.x + y * rhs.y + z * rhs.z; } Point operator^(const Point &r) const { return Point( (y*r.z - z*r.y), (z*r.x - x*r.z), (x*r.y - y*r.x) ); }
All the following member functions do change the object, and are therefore defined as non-const.
Point& operator+=(Point const& rhs) { x += rhs.x, y += rhs.y, z += rhs.z; return *this; } Point& operator-=(Point const& rhs) { x -= rhs.x, y -= rhs.y, z -= rhs.z; return *this; } Point& operator*=(float scale) { x *= scale, y *= scale, z *= scale; return *this; } Point& operator/=(float scale) { x /= scale, y /= scale, z /= scale; return *this; }
Point p1(1.0), p2(2.0); p1 + p2; // this expression is the function call: p1.operator+(p2). p1 * 2.5; // this expression is the function call: p1.operator*(2.5). 2.5 * p1; // this is wrong.
To make 2.5 * p1 valid expression, you have to define a global operator overloading, like,
Point operator*(float scale, Point const& p) { Point p2(p); return p2 * scale; }
struct Point { ... float operator[](int i) { return i==0? x : (i==1? y : z); } };
struct Point { ... friend ostream& operator<<(ostream& os, Point const& P) { return os << "(" << P.x << ", " << P.y << ", " << P.z << ")"; } };
A friend to a class is a global function or another class which has private access to the class.
// throw is basically the same as return, except if it is not caught, the program abort. void f(int a) { throw a; } void g(int b) { f(b * b); } int main() { g(3); // throw 3*3 = 9, not caught, and the program aborts. } // if change main() into. int main() { try { g(3); // throw 3 * 3 = 9; } catch(int a) { cout << "thrown val is " << a << endl; } catch(const char* msg) { cout << "thrown message is " << msg << endl; } }
Inside Point class,
struct Point { float operator[](int i) { if(i<0) throw "index under flow.\n"; if(i>2) throw "index over flow.\n" return i==0? x : (i==1? y : z); } };
and in main(),
int main() { try { Point pnt = compute point value. int idx = compute index value. pnt[idx] *= 2; } catch (const char* msg) { cout << msg << endl; exit(0); } }
std :: cout << std :: endl; // or using namespace std; cout << endl;
Now try to write a RGB class, which represents a color with its three primary components, i.e., red, green and blue. A valid color has all its three component values in the range [0.0, 1.0].
Add any necessary code into RGB class, so that the test code compiles and rund OK.
class RGB { float rgb[3]; public: /* ... */ };
1.3.6