Programming Language & Its Applications β C++ Constructs with Objects and Classes, NEC licence examination syllabus (Nepal Engineering Council).
A function that runs automatically when an object is born β the guarantee that no object exists in an invalid state.
struct FILE *f; and it holds garbage until you remember to set it; in C++ the constructor cannot be skipped, so std::string s; is always a valid empty string. This guarantee is the foundation of RAII β Resource Acquisition Is Initialisation β which is how C++ manages files, locks, sockets and memory without a garbage collector and without finally blocks. Python's __init__ and Java's constructors do the same job; what C++ adds is the matching destructor, which is the next topic and the other half of the idea. Search "RAII resource management C++"; it is arguably the single best idea in the language.#include <iostream>
#include <string>
class Student {
int roll;
std::string name;
double marks;
public:
/* 1. DEFAULT β no arguments */
Student() : roll(0), name("unknown"), marks(0.0) {
std::cout << " [default ctor]\n";
}
/* 2. PARAMETERISED */
Student(int r, const std::string &n, double m)
: roll(r), name(n), marks(m) {
std::cout << " [param ctor for " << n << "]\n";
}
/* 3. COPY β takes a const reference to its own type */
Student(const Student &other)
: roll(other.roll), name(other.name + " (copy)"),
marks(other.marks) {
std::cout << " [copy ctor]\n";
}
/* 4. CONVERTING β one argument, enables implicit
conversion unless marked explicit */
Student(int r) : roll(r), name("roll only"), marks(0.0) {
std::cout << " [converting ctor]\n";
}
void show() const {
std::cout << roll << " " << name << " " << marks << "\n";
}
};
int main() {
std::cout << "Student a;\n"; Student a;
std::cout << "Student b(101,\"Ram\",87.5);\n";
Student b(101,"Ram",87.5);
std::cout << "Student c = b;\n"; Student c = b;
std::cout << "Student d = 205;\n"; Student d = 205;
a.show(); b.show(); c.show(); d.show();
return 0;
}
Output:
Student a;
[default ctor]
Student b(101,"Ram",87.5);
[param ctor for Ram]
Student c = b;
[copy ctor]
Student d = 205;
[converting ctor]
0 unknown 0
101 Ram 87.5
101 Ram (copy) 87.5
205 roll only 0
#include <iostream>
#include <string>
class Tracer {
std::string name;
public:
Tracer(const std::string &n = "?") : name(n) {
std::cout << " ctor " << name << "\n";
}
Tracer& operator=(const Tracer &o) {
std::cout << " assign " << o.name
<< " over " << name << "\n";
name = o.name; return *this;
}
};
class UsesAssignment {
Tracer t;
public:
UsesAssignment() { /* body assignment */
t = Tracer("real"); /* ctor + ctor + assign */
}
};
class UsesInitList {
Tracer t;
public:
UsesInitList() : t("real") { } /* ONE ctor */
};
class HasConstAndRef {
const int id;
int &ref;
public:
HasConstAndRef(int i, int &r) : id(i), ref(r) {}
void show() const {
std::cout << "id=" << id << " ref=" << ref << "\n";
}
};
int main() {
std::cout << "UsesAssignment:\n"; UsesAssignment u1;
std::cout << "UsesInitList:\n"; UsesInitList u2;
int external = 77;
HasConstAndRef h(5, external);
h.show();
external = 88;
std::cout << "after external=88 -> "; h.show();
return 0;
}
Output:
UsesAssignment:
ctor ?
ctor real
assign real over ?
UsesInitList:
ctor real
id=5 ref=77
after external=88 -> id=5 ref=88
Three operations versus one β visible in the output. And note the reference member tracks external: changing external to 88 changes what h.ref reads, because ref is an alias, not a copy.
#include <iostream>
#include <cstring>
class BadString {
char *buf;
public:
BadString(const char *s) {
buf = new char[std::strlen(s) + 1];
std::strcpy(buf, s);
}
/* NO copy constructor -> the compiler generates one
that copies the POINTER. Two objects, one buffer. */
~BadString() { delete[] buf; }
void show() const { std::cout << buf << "\n"; }
void poke(char c) { buf[0] = c; }
};
class GoodString {
char *buf;
public:
GoodString(const char *s) {
buf = new char[std::strlen(s) + 1];
std::strcpy(buf, s);
}
/* DEEP copy: allocate our own buffer */
GoodString(const GoodString &o) {
buf = new char[std::strlen(o.buf) + 1];
std::strcpy(buf, o.buf);
}
~GoodString() { delete[] buf; }
void show() const { std::cout << buf << "\n"; }
void poke(char c) { buf[0] = c; }
};
int main() {
GoodString g1("hello");
GoodString g2 = g1; /* deep copy */
g2.poke('J');
std::cout << "g1: "; g1.show();
std::cout << "g2: "; g2.show();
/* BadString b1("hello"); BadString b2 = b1;
b2.poke('J'); -> b1 ALSO changes (shared buffer)
and at scope exit BOTH destructors delete[] the same
pointer: DOUBLE FREE, undefined behaviour/crash. */
return 0;
}
Output:
g1: hello
g2: Jello
unique_ptr for single ownership, shared_ptr for reference counting); Rust solved it by making ownership a checked property of the type system, so the double-free above is a compile error rather than a crash. Reading about Rust's borrow checker after understanding this page is genuinely illuminating β it is the same problem with a different answer. Search "rule of zero smart pointers" for the modern C++ approach.Create a free account to tick topics off, take notes as you read, watch the video lessons and get a day-by-day study plan built around your exam date.
Loadingβ¦