Show Lecture.StructAndClass as a slide show.
struct
is the old C way of doing things.
struct Point { int x, y; }; Point p; cout << p.x << '\n'; p.x = 8; p.y = 9; cout << p.x << ',' << p.y << '\n';
c.cc:5: warning: 'p.main()::Point::x' is used uninitialized in this function 0 8,9
public
& private
define sections of the class.
class Point { public: Point(int a, int b) { x = a; y = b; } // constructor (alias ctor) int get_x() const { return x; } // accessor int get_y() const { return y; } // accessor void go_right() { x++; } // mutator private: int x, y; // Hands off! };
To make a class
work for output, define operator<<
appropriately:
class Point { public: Point(int a, int b) { x = a; y = b; } // ctor int get_x() const { return x; } // accessor int get_y() const { return y; } // accessor void go_right() { x++; } // mutator private: // Mine! int x, y; }; ostream &operator<<(ostream &out, const Point &p) { return out << p.get_x() << ',' << p.get_y(); } int main() { Point p(12, 34); cout << p << '\n'; // invoke operator<< }
12,34
class Quoted { string s; public: Quoted(const string &word) : s(word) { } string get() const { return "“" + s + "”"; } }; ostream &operator<<(ostream &os, const Quoted &rhs) { return os << rhs.get(); } int main() { Quoted name("Bob"); cout << "Hi, " << name << "!\n"; }
Hi, “Bob”!
s
is private
const
reference argument
operator<<
not a method
public:
ostream
reference
Methods can be defined inside or outside of the class.
class Quoted { string s; public: Quoted(const string &word) : s(word) { } string get() const; // declaration only }; string Quoted::get() const { return "“" + s + "”"; } ostream &operator<<(ostream &os, const Quoted &rhs) { return os << rhs.get(); } int main() { Quoted name("Slartibartfast"); cout << "I am " << name << ".\n"; }
I am “Slartibartfast”.
public
, private
,
or protected
.
private
for a class
,
public
for a struct
.
public
: anyone can access them
private
: nobody except other methods of this class can access them
protected
: can be accessed by this class,
and by immediately derived class
class Foo { // A class, with a variable of each type public: int pub; // I’m public! private: int priv; // I’m private! protected: int prot; // I’m a little teapot, short & stout. }; int main() { Foo f; f.pub = 1; // great // f.priv = 2; // nope // f.prot = 2; // nope return f.pub; }
One class can declare another class/method/function to be its friend:
class Foo { friend class Bar; friend double Zip::zulu(int) const; friend int alpha(); friend std::ostream &operator<<(std::ostream &os, const Foo &); private: int x,y; };
class Bar
can access our data.
zulu(int) const
of the class Zip
.
alpha()
.
operator<<(ostream &, const Foo &)
.
class A
, via friend class B
,
offers friendship to class B
.
C
is D
’s friend, then D
is not necessarily
C
’s friend.
E
& F
are friends, and F
& G
are friends,
then E
& G
are strangers.
H
is I
’s friend, and I
is the parent of J
,
then H
& J
are strangers.
K
is L
’s parent, then K
& L
are strangers.
friend
declarations,
and overuse them.
friend
should be your last tool, not your first one.
friend
declarations to avoid the overhead
of method calls, then you have no faith in current compilers.
They’re quite good at inlining methods.
class Foo { public: Foo() { counter++; } ~Foo() { counter--; } int get_count() const { return counter; } private: int counter=0; }; int main() { Foo a, b, c, d, e; cout << e.get_count() << '\n'; }
1
Why doesn’t this work?
int counter=0; class Foo { public: Foo() { counter++; } ~Foo() { counter--; } int get_count() const { return counter; } }; int main() { Foo a, b, c, d, e; cout << e.get_count() << '\n'; }
5
That works, but it has an evil global variable. 👹
static int counter=0; class Foo { public: Foo() { counter++; } ~Foo() { counter--; } int get_count() const { return counter; } }; int main() { Foo a, b, c, d, e; cout << e.get_count() << '\n'; }
5
The scope of the static
global variable is only this file,
but that’s still too much.
class Foo { public: Foo() { counter++; } ~Foo() { counter--; } int get_count() const { return counter; } private: static int counter=0; }; int main() { Foo a, b, c, d, e; cout << e.get_count() << '\n'; }
c.cc:7: error: ISO C++ forbids in-class initialization of non-const static member 'Foo::counter'
That failed.
class Foo { public: Foo() { counter++; } ~Foo() { counter--; } int get_count() const { return counter; } private: static int counter; }; int Foo::counter = 0; int main() { Foo a, b, c, d, e; cout << e.get_count() << '\n'; }
5
There we go! Only a single, shared, counter, with class
scope.
static
class
variablesA static
member variable:
class
scope
public
, protected
, or private
class
static
methodsA static
method:
class
scope
public
, protected
, or private
class
const
methodsclass Ratio { public: Ratio(int a, int b) : top(a), bottom(b) { } double get_real() { return top / double(bottom); } private: int top, bottom; }; int main() { const Ratio f(355,113); cout << f.get_real() << '\n'; }
c.cc:13: error: passing 'const Ratio' as 'this' argument discards qualifiers
Oops—can’t call a non-const
method on a const
object.
const
methodsclass Ratio { public: Ratio(int a, int b) : top(a), bottom(b) { } double get_real() const { return top / double(bottom); } private: int top, bottom; }; int main() { const Ratio f(355,113); cout << f.get_real() << '\n'; }
3.14159
Making Ratio::get_real() const
fixed it. It also properly indicates
that get_real()
doesn’t change the object.
const Ratio
like that.
const
methodsclass Ratio { public: Ratio(int a, int b) : top(a), bottom(b) { } double get_real() const { return top / double(bottom); } private: int top, bottom; }; void show_ratio(Ratio r) { cout << r.get_real() << '\n'; } int main() { Ratio f(355,113); show_ratio(f); }
3.14159
That call to show_ratio
copies an object by value. Expensive!
😱
const
methodsclass Ratio { public: Ratio(int a, int b) : top(a), bottom(b) { } double get_real() const { return top / double(bottom); } private: int top, bottom; }; void show_ratio(const Ratio &r) { cout << r.get_real() << '\n'; } int main() { Ratio f(355,113); show_ratio(f); }
3.14159
r
is now const
, so get_real()
must be const
.
The compiler’s implementation of a const
method is simple; it
regards the hidden implicit pointer argument this
as pointing to a
const
object, which explains the message:
class Foo { public: void zip() {} }; int main() { const Foo f; f.zip(); }
c.cc:8: error: passing 'const Foo' as 'this' argument discards qualifiers
In Foo::zip()
, this
is type Foo *
, which can’t point to
const Foo f
.
const
is too much work; I’ll add const
later”.
const
-time.
const
to a single method.
const
methods called
by the newly-const
method.
const
causes problems with the
non-const
methods that they call.
const
as they were written, then there wouldn’t
be this “OMG, I’m doomed” moment.
/tmp
.
class
can help with that—dtors love doing cleanup.
class Tempfile { public: Tempfile() { close(mkstemp(name)); } ~Tempfile() { remove(name); } string tempname() const { return name; } private: char name[32] = "/tmp/cs253-XXXXXX"; }; Tempfile t; const string fname = t.tempname(); cout << fname << '\n';
/tmp/cs253-5T6NlZ
.tempname()
gets the name of the temporary file.
Let’s add an operator string
method:
class Tempfile { public: Tempfile() { close(mkstemp(name)); } ~Tempfile() { remove(name); } operator string() const { return name; } private: char name[32] = "/tmp/cs253-XXXXXX"; }; Tempfile t; const string fname = t; cout << fname << '\n';
/tmp/cs253-I7IeLC
Just treat it like a std::string
, and it works!