Unit 3: Constructors, Destructors and Managing File Operations - Subjective Questions
CAP455 — Object Oriented Programming Using C++ • Practice Questions with Detailed Answers
20 questions
Define a constructor in C++. Explain the important features of a constructor function.
A constructor is a special member function of a class that is invoked automatically whenever an object of that class is created. Its main purpose is to initialize the data members of the object.
Important features:
- Its name is the same as the class name.
- It has no return type, not even
void. - It is called automatically when an object is created.
- It normally initializes the data members and acquires required resources.
- It can be overloaded by defining constructors with different parameter lists.
- It can have default arguments.
- It is usually declared in the
publicsection so that objects can be created outside the class. - A constructor cannot be
virtual,static, or inherited in the ordinary sense. - Its address cannot normally be taken like that of an ordinary member function.
Example:
class Student {
int roll;
public:
Student() {
roll = 0;
}
};Here, Student() is invoked automatically and initializes roll to zero.
What is a default constructor? Explain its operation with a suitable C++ program.
A default constructor is a constructor that can be called without supplying any arguments. It may have no parameters, or all its parameters may have default values.
#include <iostream>
using namespace std;
class Point {
int x, y;
public:
Point() {
x = 0;
y = 0;
}
void display() const {
cout << x << ", " << y;
}
};
int main() {
Point p;
p.display();
return 0;
}Operation:
- When
Point p;is executed,Point()is invoked automatically. - The members
xandyare initialized to zero. - If a class has no user-declared constructor, the compiler may generate an implicit default constructor.
- Once a parameterized constructor is declared, the compiler does not automatically provide a no-argument constructor. A default constructor must then be explicitly defined if no-argument object creation is required.
Distinguish between a constructor and a normal member function in C++.
A constructor differs from a normal member function in the following ways:
| Basis | Constructor | Normal member function |
|---|---|---|
| Name | Must have the same name as the class | May have any valid identifier |
| Return type | Has no return type | Must have a return type, including void where appropriate |
| Invocation | Invoked automatically during object creation | Usually called explicitly through an object |
| Purpose | Initializes an object or acquires resources | Performs operations on an existing object |
| Frequency | Runs once for each construction of an object | May be called any number of times |
| Overloading | Can be overloaded | Can also be overloaded |
| Virtual behavior | Cannot be virtual | May be virtual |
| Inheritance | Is not inherited like an ordinary member function | May be inherited, subject to access rules |
Example:
class Demo {
public:
Demo() { // Constructor
// Initialization
}
void show() { // Normal member function
// Regular operation
}
};Demo() runs automatically when an object is created, whereas show() must be called explicitly.
Explain parameterized constructors and constructor overloading with an appropriate C++ example.
A parameterized constructor accepts one or more arguments and uses them to initialize an object with user-supplied values. Defining multiple constructors with different parameter lists is known as constructor overloading.
#include <iostream>
using namespace std;
class Rectangle {
double length, width;
public:
Rectangle() : length(0), width(0) {}
Rectangle(double side) : length(side), width(side) {}
Rectangle(double l, double w) : length(l), width(w) {}
double area() const {
return length * width;
}
};
int main() {
Rectangle r1;
Rectangle r2(5);
Rectangle r3(6, 4);
cout << r1.area() << '\n';
cout << r2.area() << '\n';
cout << r3.area() << '\n';
}Explanation:
Rectangle()creates a rectangle with zero dimensions.Rectangle(5)creates a square of side 5.Rectangle(6, 4)creates a rectangle of length 6 and width 4.- The compiler selects the appropriate constructor by matching the number and types of arguments.
- Constructor overloading provides multiple convenient ways to initialize objects.
What is a copy constructor? Explain when it is invoked and illustrate deep copying with a C++ program.
A copy constructor creates a new object as a copy of an existing object of the same class. Its usual declaration is:
ClassName(const ClassName& source);It is commonly invoked when:
- A new object is initialized from an existing object.
- An object is passed to a function by value.
- An object is returned from a function by value, although copy elision may remove the call.
A class managing dynamic memory should generally perform a deep copy:
#include <cstring>
#include <iostream>
using namespace std;
class Text {
char* data;
public:
Text(const char* value) {
data = new char[strlen(value) + 1];
strcpy(data, value);
}
Text(const Text& other) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
void display() const {
cout << data;
}
~Text() {
delete[] data;
}
};Deep copy versus shallow copy:
- A shallow copy copies only the pointer, causing two objects to refer to the same memory.
- A deep copy allocates separate memory and copies the actual content.
- Deep copying prevents aliasing, dangling pointers, and repeated deletion of the same memory.
- For complete resource management, copy assignment should also be implemented; this is part of the Rule of Three.
Explain constructor initializer lists. Why are they preferred or required in certain situations?
A constructor initializer list initializes data members before the constructor body begins. It appears after the constructor parameter list and before the body.
class Employee {
const int id;
int age;
public:
Employee(int employeeId, int employeeAge)
: id(employeeId), age(employeeAge) {}
};Advantages:
- Members are initialized directly instead of being default-initialized and then assigned.
- It can improve efficiency, especially for class-type members.
- It is required for
constdata members because they cannot be assigned after initialization. - It is required for reference members because a reference must be bound at initialization.
- It is used to invoke a specific base-class constructor.
- It is required when a member object has no accessible default constructor.
Important rule: Members are initialized in the order in which they are declared in the class, not in the order in which they appear in the initializer list.
class Example {
int a;
int b;
public:
Example(int value) : b(value), a(b) {}
};The example is problematic because a is initialized before b, following declaration order. Therefore, initializer lists should normally be written in the same order as the member declarations.
Describe a constructor with default arguments. Discuss its benefits and the ambiguity that may occur when it is combined with a default constructor.
A constructor with default arguments assigns default values to one or more of its parameters. It can initialize objects using different numbers of supplied arguments.
class Box {
int length, width, height;
public:
Box(int l = 1, int w = 1, int h = 1)
: length(l), width(w), height(h) {}
};Possible object declarations are:
Box b1; // 1, 1, 1
Box b2(5); // 5, 1, 1
Box b3(5, 4); // 5, 4, 1
Box b4(5, 4, 3); // 5, 4, 3Benefits:
- Reduces the need for several similar overloaded constructors.
- Provides convenient standard initial values.
- Allows initialization with zero, some, or all arguments.
Possible ambiguity:
class Box {
public:
Box() {}
Box(int l = 1) {}
};For Box b;, both constructors can be called without arguments, so the call is ambiguous. To avoid this, do not define both a no-argument constructor and another constructor whose every parameter has a default value.
Define a destructor. Explain its characteristics, order of execution, and role in resource management.
A destructor is a special member function that is invoked automatically when an object's lifetime ends. Its name is the class name preceded by a tilde.
class Resource {
int* data;
public:
Resource() {
data = new int[100];
}
~Resource() {
delete[] data;
}
};Characteristics:
- It has the form
~ClassName(). - It has no return type and accepts no parameters.
- A class can have only one destructor, so destructors cannot be overloaded.
- It is called automatically for local objects at the end of their scope.
- It is called for dynamically allocated objects when
deleteis used. - It releases resources such as dynamic memory, files, locks, and network handles.
Destruction order:
- Local objects are destroyed in the reverse order of their construction.
- Array elements are destroyed in reverse element order.
- In inheritance, the derived-class destructor runs before the base-class destructor.
- Member objects are destroyed after the enclosing destructor body, in reverse declaration order.
A base class intended for polymorphic deletion should have a virtual destructor. This ensures that deleting a derived object through a base pointer invokes both the derived and base destructors.
Explain how files are opened and closed in C++ using constructors and the open() and close() functions.
C++ file processing is provided through <fstream> using ifstream, ofstream, and fstream.
Opening through a stream constructor:
#include <fstream>
using namespace std;
ifstream input("input.txt");
ofstream output("output.txt");
fstream file("data.txt", ios::in | ios::out);Opening with open():
fstream file;
file.open("data.txt", ios::in | ios::out);
if (!file.is_open()) {
// Handle opening failure
}The open() approach is useful when the file name or mode is decided during program execution, or when the same stream object is reused.
Closing a file:
file.close();Closing a file:
- Flushes buffered output.
- Releases the operating-system file handle.
- Breaks the association between the stream and the file.
- Allows the stream to be associated with another file later.
A stream's destructor closes an open file automatically, but explicit close() is useful when errors must be checked or the file needs to be closed before the stream object goes out of scope.
Describe the different file opening modes available in C++. Explain how multiple modes can be combined.
File modes are flags from ios or ios_base that specify how a file should be opened.
| Mode | Purpose |
|---|---|
ios::in |
Opens a file for input |
ios::out |
Opens a file for output |
ios::app |
Places every write operation at the end of the file |
ios::ate |
Initially places the file position at the end, but later seeking is allowed |
ios::trunc |
Discards existing file contents when opening for output |
ios::binary |
Opens the file in binary mode |
Modes can be combined using the bitwise OR operator |:
fstream file("records.dat", ios::in | ios::out | ios::binary);Important distinctions:
ios::appforces every write to occur at the end.ios::ateonly selects the end as the initial position; the program may seek elsewhere afterward.ios::truncremoves old content, so it should be used carefully.- Binary mode prevents text-mode translations such as newline conversion on systems that perform them.
The selected mode must match the intended operation. For example, a file opened only with ios::in cannot normally be used for output.
Explain the important state-checking, positioning, and data-transfer functions provided by C++ file streams.
C++ file streams provide functions for checking stream state, transferring data, and controlling file positions.
State-checking functions:
is_open()checks whether a stream is associated with a file.good()returns true when no error flag is set.eof()checks whether the end-of-file flag is set.fail()detects a formatting or logical input/output failure.bad()detects a serious input/output error.clear()resets the stream's error flags.
Data-transfer functions:
get()reads one character or a sequence of characters.getline()reads a complete line.put()writes one character.read()reads a block of bytes.write()writes a block of bytes.- The operators
>>and<<perform formatted input and output.
Positioning functions:
tellg()returns the current input, or get, position.tellp()returns the current output, or put, position.seekg()changes the input position.seekp()changes the output position.
Example:
fstream file("data.dat", ios::in | ios::out | ios::binary);
file.seekg(0, ios::end);
streampos size = file.tellg();Here, the input position is moved to the end and tellg() obtains the file size in bytes for a binary file.
Explain formatted and unformatted reading and writing of text files with suitable examples.
Formatted input and output use operators such as >> and <<. Values are converted between their internal representations and textual representations.
#include <fstream>
#include <string>
using namespace std;
ofstream out("student.txt");
out << 101 << ' ' << "Asha" << ' ' << 92.5 << '\n';
out.close();
ifstream in("student.txt");
int roll;
string name;
double mark;
in >> roll >> name >> mark;Unformatted input and output transfer characters or blocks without numeric formatting. Common functions include get(), put(), and getline().
ifstream source("input.txt");
ofstream target("copy.txt");
char ch;
while (source.get(ch)) {
target.put(ch);
}Reading complete lines:
string line;
while (getline(source, line)) {
// Process line
}Key points:
operator>>normally skips leading whitespace and stops string input at whitespace.getline()can read spaces and stops at a delimiter, which is newline by default.- Reading should be controlled by the input operation itself, such as
while (in >> value), rather than bywhile (!in.eof()). - Streams should be checked for opening and input/output failures.
What is sequential file access? Describe its working, advantages, limitations, and a typical processing algorithm.
Sequential access processes records in order, beginning at the current file position and advancing from one record to the next. It is similar to reading a list from beginning to end.
Example:
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
ifstream file("marks.txt");
int roll;
string name;
double mark;
while (file >> roll >> name >> mark) {
cout << roll << ' ' << name << ' ' << mark << '\n';
}
}Working:
- Open the file for input.
- Position the stream at the beginning.
- Read one record.
- Process the record.
- Continue until reading fails or the end is reached.
- Close the file.
Advantages:
- Simple to implement.
- Efficient when all or most records must be processed.
- Suitable for logs, reports, and text files.
Limitations:
- Accessing a record near the end may require reading all preceding records.
- Updating one record in a variable-length text file may require creating a new file.
- It is inefficient for repeated searches in a large file.
Sequential access is therefore most suitable for batch processing and ordered traversal.
Explain random access file processing using seekg(), seekp(), tellg(), and tellp(). Derive the position of the th fixed-size record.
Random access allows a program to move directly to a required position instead of processing every earlier record. It is especially effective for fixed-size binary records.
Positioning functions:
seekg(offset, origin)moves the input position.seekp(offset, origin)moves the output position.tellg()reports the current input position.tellp()reports the current output position.
The origins are ios::beg, ios::cur, and ios::end.
If records are numbered from 1 and each record occupies sizeof(Record) bytes, the byte position of the th record is:
Example:
struct Record {
int id;
char name[30];
double salary;
};
fstream file("records.dat", ios::in | ios::out | ios::binary);
int n = 5;
file.seekg((n - 1) * static_cast<streamoff>(sizeof(Record)), ios::beg);
Record r;
file.read(reinterpret_cast<char*>(&r), sizeof(r));To update the same record:
file.clear();
file.seekp((n - 1) * static_cast<streamoff>(sizeof(Record)), ios::beg);
file.write(reinterpret_cast<const char*>(&r), sizeof(r));Random access is unreliable for ordinary variable-length text records because their byte positions cannot be derived from a constant record size. An index or a scan may be needed in that case.
Explain binary file operations in C++. Compare binary files with text files and demonstrate the use of read() and write().
A binary file stores data as bytes, generally in a representation close to its in-memory form. It is opened using ios::binary.
#include <fstream>
using namespace std;
struct Product {
int id;
char name[30];
double price;
};
int main() {
Product p = {1, "Keyboard", 850.0};
ofstream out("product.dat", ios::binary);
out.write(reinterpret_cast<const char*>(&p), sizeof(p));
out.close();
Product result{};
ifstream in("product.dat", ios::binary);
in.read(reinterpret_cast<char*>(&result), sizeof(result));
return 0;
}Text versus binary files:
| Text files | Binary files |
|---|---|
| Store human-readable characters | Store raw byte sequences |
| Use formatted operators commonly | Use read() and write() commonly |
| Numeric conversion is required | Often faster for fixed-format data |
| Easier to inspect and edit | Usually more compact |
| More portable when a defined text format is used | Raw layouts may depend on platform, compiler, padding, and byte order |
Raw binary writing should generally be limited to trivially copyable, fixed-layout data. Objects containing std::string, pointers, virtual functions, or dynamic resources cannot be saved correctly by simply writing their memory bytes. Such objects require explicit serialization.
Describe how objects of a class can be stored in and retrieved from a file. Explain why direct binary dumping is unsafe for some classes.
Class objects can be stored using either formatted serialization or binary serialization.
A safe formatted approach writes individual data members:
#include <fstream>
#include <iomanip>
#include <string>
using namespace std;
class Student {
int roll;
string name;
double mark;
public:
Student(int r = 0, string n = "", double m = 0)
: roll(r), name(n), mark(m) {}
void save(ostream& out) const {
out << roll << ' ' << quoted(name) << ' ' << mark << '\n';
}
bool load(istream& in) {
return static_cast<bool>(in >> roll >> quoted(name) >> mark);
}
};Usage:
Student s(10, "Ravi Kumar", 88.5);
ofstream out("students.txt");
s.save(out);
Student copy;
ifstream in("students.txt");
copy.load(in);Why direct memory dumping may be unsafe:
std::stringusually contains internal pointers rather than all characters directly in the object.- Pointer values are meaningless when the file is read in another execution.
- Objects may contain padding bytes.
- Virtual functions can introduce implementation-specific pointers.
- Byte order and type sizes may differ across systems.
- Private invariants may need validation during loading.
Robust class serialization writes each logical field in a documented format and reconstructs the object through controlled member functions or constructors.
Explain file operations with structures. Write a C++ approach to append, display, and search fixed-size structure records in a binary file.
A structure containing fixed-size, trivially copyable members can be stored as a binary record. Consider:
#include <cstring>
#include <fstream>
#include <iostream>
using namespace std;
struct Employee {
int id;
char name[30];
double salary;
};Appending a record:
Employee e{101, "Anita", 45000.0};
ofstream out("employees.dat", ios::binary | ios::app);
out.write(reinterpret_cast<const char*>(&e), sizeof(e));Displaying all records:
ifstream in("employees.dat", ios::binary);
Employee item;
while (in.read(reinterpret_cast<char*>(&item), sizeof(item))) {
cout << item.id << ' ' << item.name << ' ' << item.salary << '\n';
}Searching by ID:
ifstream searchFile("employees.dat", ios::binary);
int requiredId = 101;
bool found = false;
while (searchFile.read(reinterpret_cast<char*>(&item), sizeof(item))) {
if (item.id == requiredId) {
found = true;
break;
}
}Precautions:
- Use fixed-size arrays instead of pointers for direct raw storage.
- Check whether the file opened successfully.
- Let the
read()operation control the loop. - Raw structure files may not be portable because of padding, byte order, and type-size differences.
- For long-term or cross-platform storage, serialize each field in a defined format.
Compare sequential access and random access techniques for file processing.
Sequential and random access differ mainly in how records are located and processed.
| Basis | Sequential access | Random access |
|---|---|---|
| Processing order | Records are processed one after another | A required record can be accessed directly |
| Positioning | Position advances naturally after each operation | Uses seekg() or seekp() |
| Record format | Works with fixed- or variable-length records | Most convenient with fixed-size records |
| Search speed | May require scanning many records | Direct access can be much faster |
| Implementation | Simple | Requires position calculations or an index |
| Typical uses | Logs, reports, complete-file processing | Databases, account records, inventory updates |
| Text suitability | Very suitable | Difficult when text records have variable lengths |
| Updating | May require rewriting the file | Fixed-size records can be updated in place |
For a fixed-size record numbered from 1, its position can be calculated as:
Sequential access is preferable when most records must be processed. Random access is preferable when individual records must be repeatedly retrieved or updated.
Explain stream error states and proper error handling during file operations. Why is while (!file.eof()) considered incorrect?
File streams maintain flags that describe the result of input/output operations.
Major state flags and functions:
good()indicates that no error state is active.eof()indicates that an attempt to read reached the end of the file.fail()indicates a formatting failure or another recoverable failure.bad()indicates a serious input/output error.clear()resets the error flags.
Correct formatted input loop:
int value;
while (file >> value) {
// Process value
}Correct binary input loop:
Record r;
while (file.read(reinterpret_cast<char*>(&r), sizeof(r))) {
// Process complete record
}while (!file.eof()) is incorrect because the end-of-file flag is set only after an input operation attempts to read beyond the available data. The loop may therefore execute once using stale, incomplete, or invalid data.
Seeking after a failure:
file.clear();
file.seekg(0, ios::beg);The error state must normally be cleared before another input operation or seek can succeed. Programs should also check is_open() after opening and verify critical write operations before assuming that data was stored successfully.
Design and explain a C++ class-based file system that adds, displays, searches, and updates student records using random access.
A random-access student file can use fixed-size records so that each record has a predictable byte position.
#include <cstring>
#include <fstream>
#include <iostream>
using namespace std;
class Student {
int roll;
char name[30];
double mark;
public:
Student(int r = 0, const char* n = "", double m = 0)
: roll(r), mark(m) {
strncpy(name, n, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
}
int getRoll() const { return roll; }
void display() const {
cout << roll << ' ' << name << ' ' << mark << '\n';
}
void setMark(double value) { mark = value; }
};Add a record:
void add(const Student& s) {
ofstream out("students.dat", ios::binary | ios::app);
out.write(reinterpret_cast<const char*>(&s), sizeof(s));
}Display all records:
void displayAll() {
ifstream in("students.dat", ios::binary);
Student s;
while (in.read(reinterpret_cast<char*>(&s), sizeof(s))) {
s.display();
}
}Search and update by physical record number :
void update(int n, double newMark) {
fstream file("students.dat", ios::in | ios::out | ios::binary);
streamoff position = (n - 1) * static_cast<streamoff>(sizeof(Student));
Student s;
file.seekg(position, ios::beg);
if (file.read(reinterpret_cast<char*>(&s), sizeof(s))) {
s.setMark(newMark);
file.seekp(position, ios::beg);
file.write(reinterpret_cast<const char*>(&s), sizeof(s));
}
}Explanation and limitations:
- Appending places each new record at the end.
- Sequential reading displays or searches records.
- A known physical record number permits direct updating using the offset formula.
- Searching by roll number still requires a scan unless a separate index maps roll numbers to positions.
- The demonstrated raw-object technique is suitable only because the class has a simple fixed layout and no pointers,
std::string, virtual functions, or dynamic resources. - A production system should use explicit serialization for portability and data validation.
Define a constructor in C++. Explain the important features of a constructor function.
A constructor is a special member function of a class that is invoked automatically whenever an object of that class is created. Its main purpose is to initialize the data members of the object.
Important features:
- Its name is the same as the class name.
- It has no return type, not even
void. - It is called automatically when an object is created.
- It normally initializes the data members and acquires required resources.
- It can be overloaded by defining constructors with different parameter lists.
- It can have default arguments.
- It is usually declared in the
publicsection so that objects can be created outside the class. - A constructor cannot be
virtual,static, or inherited in the ordinary sense. - Its address cannot normally be taken like that of an ordinary member function.
Example:
class Student {
int roll;
public:
Student() {
roll = 0;
}
};Here, Student() is invoked automatically and initializes roll to zero.
Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →