Unit 3: Data Files, Constructors and Destructors - Subjective Questions
CSE202 — Object Oriented Programming • Practice Questions with Detailed Answers
20 questions
Define a file and explain how files are opened and closed in C++ using file streams.
A file is a named collection of data stored permanently on a secondary storage device. Unlike variables in memory, file data remains available after a program terminates.
C++ provides the following stream classes in the <fstream> header:
ifstream: Opens a file for input or reading.ofstream: Opens a file for output or writing.fstream: Opens a file for both input and output.
A file can be opened through a constructor:
ifstream inputFile("data.txt");
ofstream outputFile("result.txt");
It can also be opened using the open() function:
fstream file;
file.open("data.txt", ios::in | ios::out);
The file should be checked after opening:
if (!file.is_open()) {
cout << "Unable to open file";
}
The close() function disconnects the stream from the file:
file.close();
Closing a file ensures that buffered output is written, system resources are released, and the stream can be associated with another file.
Explain the different file opening modes available in C++. How can multiple modes be combined?
File modes specify how a file is to be opened and processed. They are passed as the second argument to a stream constructor or the open() function.
ios::in: Opens a file for reading.ios::out: Opens a file for writing. Existing content may be discarded by default for an output stream.ios::app: Opens a file in append mode. Every write operation occurs at the end of the file.ios::ate: Opens the file and initially positions the pointer at the end. The pointer may later be moved elsewhere.ios::trunc: Deletes existing file contents when the file is opened.ios::binary: Opens the file in binary mode without text-mode character translations.
Multiple modes are combined using the bitwise OR operator |:
fstream file("records.dat", ios::in | ios::out | ios::binary);
ios::app and ios::ate differ because app forces all output to the end, whereas ate only sets the initial position to the end.
Describe the important file stream functions used to control file processing and detect errors in C++.
Important file stream functions include:
open(filename, mode): Associates a stream with a file in the specified mode.close(): Closes the associated file and releases its resources.is_open(): Returnstrueif a file is currently associated with the stream.get(): Reads a single character, including whitespace.put(ch): Writes a single character.getline(): Reads a complete line or reads characters up to a delimiter.read(address, size): Reads a block of bytes from a binary file.write(address, size): Writes a block of bytes to a binary file.seekg()andseekp(): Reposition the input and output pointers.tellg()andtellp(): Return the current input and output positions.eof(): Indicates that an attempt has been made to read beyond the end of the file.fail(): Indicates that a logical input/output operation failed.bad(): Indicates a serious stream error.good(): Indicates that no stream error flags are set.clear(): Resets stream error flags so that processing can continue when appropriate.
Explain how formatted and unformatted data are written to and read from text files in C++. Give suitable examples.
Formatted file input and output use the extraction and insertion operators. Values are converted between their internal representations and readable text.
ofstream out("student.txt");
out << 101 << ' ' << "Anita" << ' ' << 86.5 << '\n';
out.close();
ifstream in("student.txt");
int roll;
string name;
double score;
in >> roll >> name >> score;
The extraction operator normally skips leading whitespace and separates input into tokens.
Unformatted file input and output process characters or blocks more directly:
get(ch)reads one character, including whitespace.put(ch)writes one character.-
getline(in, text)reads an entire line into astring.string line;
while (getline(in, line)) {
cout << line << '\n';
}
Formatted operations are convenient for values such as numbers and words. Unformatted operations are more suitable when spaces, line boundaries, or exact character sequences must be preserved.
Distinguish between sequential-access and random-access file processing.
Sequential access processes records in their stored order, usually from the beginning to the end.
- Each record is read after the preceding record.
- It is simple to implement.
- It is suitable for logs, reports, and complete-file processing.
- Accessing a record near the end may require reading all earlier records.
Random access allows the program to move directly to a selected file position.
- It uses functions such as
seekg(),seekp(),tellg(), andtellp(). - It is efficient when individual records must be retrieved or updated.
- It commonly uses fixed-size records in a binary file.
- The byte position of record number , when numbering starts at zero, is:
Sequential access is preferable when most or all records are processed in order. Random access is preferable for applications such as account, inventory, or employee-record systems where a specific record must be reached quickly.
Describe the functions used to manipulate the get pointer and put pointer during random-access file processing.
A file stream maintains position indicators:
- The get pointer identifies where the next input operation will read.
- The put pointer identifies where the next output operation will write.
The relevant functions are:
seekg(offset, direction): Moves the get pointer.seekp(offset, direction): Moves the put pointer.tellg(): Returns the current position of the get pointer.tellp(): Returns the current position of the put pointer.
The possible reference directions are:
ios::beg: Beginning of the file.ios::cur: Current pointer position.ios::end: End of the file.
Examples:
file.seekg(0, ios::beg); // Move input pointer to beginning
file.seekp(20, ios::beg); // Move output pointer to byte 20
file.seekg(-10, ios::end); // Move 10 bytes before the end
streampos position = file.tellg();
Before seeking after an unsuccessful read or end-of-file condition, clear() may be required to reset the stream state.
Explain binary file operations in C++. Develop a short example that writes and reads a fixed-size record.
A binary file stores data as sequences of bytes rather than as formatted characters. It is opened using ios::binary. Binary input and output commonly use read() and write().
struct Record {
int id;
char name[30];
double salary;
};
Record first = {1, "Ravi", 45000.0};
ofstream out("records.dat", ios::binary);
out.write(reinterpret_cast<const char*>(&first), sizeof(first));
out.close();
Record second{};
ifstream in("records.dat", ios::binary);
in.read(reinterpret_cast<char*>(&second), sizeof(second));
in.close();
Key points are:
write()expects a pointer to bytes and the number of bytes to write.read()places the requested bytes into an object.- Binary files are generally compact and fast.
- Raw object storage is reliable only for simple fixed-layout data without pointers, virtual functions, or dynamically managed resources.
- Binary representations may not be portable across systems because of byte order, padding, type sizes, and compiler-specific object layouts.
Portable applications should serialize each field in a defined format instead of dumping arbitrary objects directly.
Compare text files and binary files with respect to representation, size, speed, portability, and usability.
Text files:
- Store values as readable character sequences.
- Can be inspected and edited using ordinary text editors.
- Usually require formatting and parsing during output and input.
- May occupy more space; for example, an integer is stored as its decimal digits.
- Are often more portable when encoding and numeric formats are clearly defined.
- Are suitable for configuration files, reports, and data exchange.
Binary files:
- Store data as bytes in a program-defined representation.
- Are not directly readable by people.
- Can provide faster input and output because less conversion is required.
- Often use less storage for numeric data.
- Support efficient random access when records have a fixed size.
- May be system-dependent because of padding, byte order, and type representation.
Thus, text files favor readability and interoperability, while binary files favor compactness, speed, and direct access. The correct choice depends on the application's persistence and portability requirements.
Explain how a class can perform file operations. Illustrate a suitable design for storing and retrieving class data.
File operations can be encapsulated in class member functions so that persistence behavior remains associated with the class.
class Student {
int roll;
string name;
public:
Student(int r = 0, string n = "") : roll(r), name(n) {}
void save(ostream& out) const {
out << roll << '\n' << name << '\n';
}
bool load(istream& in) {
if (!(in >> roll)) return false;
in.ignore(numeric_limits<streamsize>::max(), '\n');
return static_cast<bool>(getline(in, name));
}
};
Usage:
Student s1(10, "Asha Kumar");
ofstream out("student.txt");
s1.save(out);
Student s2;
ifstream in("student.txt");
s2.load(in);
This design has several advantages:
- Data representation is controlled by the class.
- Private members remain protected from direct external access.
- Validation can be performed while loading.
- Passing
istreamandostreamreferences makes the functions usable with both files and other streams.
Objects containing string, pointers, or virtual functions should not normally be stored using a raw binary memory dump. Their logical fields should be serialized individually.
Describe how structures are used with files. Explain both text-based and binary storage of structure records.
A structure groups related data fields into one record. For example:
struct Employee {
int id;
char name[30];
double salary;
};
For text storage, each field is written in a chosen textual format:
out << employee.id << ','
<< employee.name << ','
<< employee.salary << '\n';
During reading, the fields must be parsed using the same delimiter and order. Text storage is readable but requires conversion and careful handling of delimiters.
For binary storage, a simple fixed-layout structure can be written as bytes:
out.write(reinterpret_cast<const char*>(&employee), sizeof(employee));
It can be read using:
in.read(reinterpret_cast<char*>(&employee), sizeof(employee));
Binary structure records are convenient for fixed-size random access. However, raw binary storage may include padding and system-dependent representations. Structures containing pointers, string, dynamic arrays, or other non-trivial members must be serialized field by field because pointer values and internal object data are not valid persistent representations.
What are constructors and destructors? Why are they called manager functions of a class?
A constructor is a special member function that initializes an object when it is created.
- Its name is the same as the class name.
- It has no return type, not even
void. - It is invoked automatically during object creation.
- It may be overloaded to support different forms of initialization.
- It can acquire resources such as memory, files, or locks.
A destructor is a special member function that runs automatically when an object is destroyed.
- Its name is the class name preceded by
~. - It has no return type and accepts no parameters.
- A class has only one destructor.
- It releases resources owned by the object.
They are called manager functions because they manage the lifetime of objects. Constructors establish a valid initial state and may acquire resources, while destructors perform final cleanup and release those resources. This supports the C++ principle called Resource Acquisition Is Initialization, in which resource ownership is tied to object lifetime.
Define a default constructor. Under what conditions does the compiler generate one automatically?
A default constructor is a constructor that can be called without supplying any arguments. It may have no parameters or may have parameters for which every argument has a default value.
class Point {
int x, y;
public:
Point() : x(0), y(0) {}
};
It is invoked in declarations such as:
Point p;
If a class declares no constructors, the compiler implicitly declares a default constructor when one is needed. The generated constructor performs default initialization of base classes and class-type members, but built-in data members such as uninitialized int values are not automatically set to zero in ordinary default initialization.
If the programmer declares any constructor, the compiler does not automatically provide the usual implicit default constructor. It can be requested explicitly:
Point() = default;
A default constructor is important when creating arrays of objects or using containers and operations that require default-constructible elements.
Explain a constructor with default arguments. How does it differ from an ordinary default constructor, and what ambiguity can arise?
A constructor with default arguments assigns default values to one or more parameters:
class Box {
double length, width, height;
public:
Box(double l = 1.0, double w = 1.0, double h = 1.0)
: length(l), width(w), height(h) {}
};
It supports several forms of construction:
Box a; // Uses all default values
Box b(5.0); // Defaults width and height
Box c(5.0, 4.0); // Defaults height
Box d(5.0, 4.0, 3.0);
Because Box() can be called without arguments, it also qualifies as a default constructor. An ordinary no-argument constructor has no parameters, while this constructor has parameters whose values may be omitted.
A class should not generally declare both of the following:
Box();
Box(double l = 1.0);
The declaration Box object; would match both constructors, causing an ambiguous call. Constructor overloads and default arguments must therefore be designed together carefully.
Describe a parameterized constructor and explain constructor overloading with an example.
A parameterized constructor accepts one or more arguments and uses them to initialize an object with caller-supplied values.
class Rectangle {
double length;
double width;
public:
Rectangle() : length(0), width(0) {}
Rectangle(double side) : length(side), width(side) {}
Rectangle(double l, double w) : length(l), width(w) {}
};
Examples:
Rectangle empty;
Rectangle square(5.0);
Rectangle room(6.0, 4.0);
Defining several constructors with different parameter lists is called constructor overloading. The compiler selects the appropriate constructor by applying normal overload-resolution rules to the number and types of arguments.
Parameterized constructors provide controlled initialization and help ensure that objects begin in valid states. A single-argument constructor may permit implicit conversion from its parameter type to the class type. When that conversion is undesirable, it should be declared explicit:
explicit Rectangle(double side); Explain the role and behavior of a destructor. In what order are destructors invoked for local objects, members, and derived objects?
A destructor performs cleanup when an object's lifetime ends. Its general form is:
class FileOwner {
fstream file;
public:
~FileOwner() {
if (file.is_open()) file.close();
}
};
A destructor is invoked automatically:
- When a local object leaves its scope.
- When an exception causes stack unwinding.
- When a dynamically allocated object is destroyed using
delete. - When a containing object is destroyed.
- At program termination for objects with static storage duration.
Destruction takes place in the reverse order of construction:
- Local objects in the same scope are destroyed in reverse creation order.
- A derived class destructor body executes before its base class destructor.
- Members are destroyed after the containing destructor body, in reverse order of their declaration.
- Base classes are destroyed after the derived object's members.
A base class intended for polymorphic deletion should normally have a virtual destructor. This ensures that deleting an object through a base-class pointer invokes the complete derived-class destruction sequence.
Define a copy constructor. State when it is invoked and demonstrate its general syntax.
A copy constructor creates a new object from an existing object of the same class. Its common form is:
ClassName(const ClassName& other);
The argument is passed by reference to avoid recursively calling the copy constructor, and it is normally const so that constant and temporary objects can be copied.
Example:
class Number {
int value;
public:
Number(int v = 0) : value(v) {}
Number(const Number& other) : value(other.value) {}
};
Number a(10);
Number b(a);
Number c = a;
A copy constructor may be invoked when:
- A new object is initialized from an existing object.
- An object is passed to a function by value.
- An object is returned by value, although copy elision may remove the actual copy.
- An exception object is copied in relevant contexts.
If no copy constructor is declared, the compiler may generate one that copies each base and member. This memberwise copy is sufficient for value-like members but may be unsafe for classes that directly own dynamic resources.
Distinguish between a shallow copy and a deep copy. Explain why a user-defined copy constructor may be necessary.
A shallow copy copies member values directly. If a member is a raw pointer, both objects receive the same address and therefore refer to the same resource.
This can cause:
- Accidental sharing of mutable data.
- Dangling pointers when one object releases the resource.
- Double deletion when both destructors release the same allocation.
A deep copy allocates a separate resource and copies the resource's contents:
class Buffer {
size_t size;
int* data;
public:
Buffer(size_t n) : size(n), data(new int[n]{}) {}
Buffer(const Buffer& other)
: size(other.size), data(new int[other.size]) {
copy(other.data, other.data + size, data);
}
~Buffer() {
delete[] data;
}
};
A user-defined copy constructor is necessary when the compiler-generated memberwise copy does not represent the intended ownership semantics. A resource-owning class that defines a destructor and copy constructor generally also needs a copy-assignment operator, which is known as the Rule of Three. In modern C++, standard containers and smart pointers are preferred because they reduce the need for manual resource management.
What is a constructor initializer list? Explain why it is preferred over assignment inside the constructor body.
A constructor initializer list appears after the constructor parameter list and before its body. It initializes base classes and data members directly.
class Student {
const int roll;
string name;
double score;
public:
Student(int r, string n, double s)
: roll(r), name(n), score(s) {}
};
Initializer lists are preferred because:
- Members are constructed directly with the desired values.
- Assignment in the body first default-constructs a member and then assigns another value, which may be less efficient.
constdata members must be initialized in an initializer list.- Reference members must be initialized in an initializer list.
- Base-class constructors are selected through initializer lists.
- Members whose types lack default constructors must be initialized this way.
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. Therefore, initializer lists should normally follow declaration order to avoid misleading code and dependency errors.
Develop a C++ approach for randomly accessing and updating a structure record in a binary file. Explain the position calculation and major error checks.
Suppose a binary file contains fixed-size employee records:
struct Employee {
int id;
char name[30];
double salary;
};
To update record number n, where numbering begins at zero:
fstream file("employees.dat",
ios::in | ios::out | ios::binary);
if (!file) {
cerr << "File opening failed";
return;
}
size_t n = 4;
Employee employee{};
streamoff position = static_cast<streamoff>(n * sizeof(Employee));
file.seekg(position, ios::beg);
if (!file.read(reinterpret_cast<char*>(&employee), sizeof(employee))) {
cerr << "Record could not be read";
return;
}
employee.salary += 5000.0;
file.seekp(position, ios::beg);
if (!file.write(reinterpret_cast<const char*>(&employee), sizeof(employee))) {
cerr << "Record could not be written";
}
The record offset is:
This method works because every record has the same byte size. The program should verify that the file opened, the requested position is within the file, and both seek and input/output operations succeeded. If a previous operation set an error flag, clear() must be called before another seek. Raw structure files also have portability limitations caused by padding and machine-dependent representations.
Design and explain a class that combines constructors, a copy constructor, a destructor, initializer lists, and file operations to manage a file resource.
A file-managing class can use object lifetime to control the stream resource:
class TextFile {
string filename;
fstream stream;
public:
TextFile(const string& name, ios::openmode mode)
: filename(name), stream(name, mode) {
if (!stream) {
throw runtime_error("Cannot open file: " + filename);
}
}
TextFile(const TextFile&) = delete;
TextFile& operator=(const TextFile&) = delete;
~TextFile() {
if (stream.is_open()) {
stream.close();
}
}
fstream& get() {
return stream;
}
};
Explanation:
- The parameterized constructor receives a filename and mode.
- The initializer list constructs
filenameand opensstreamdirectly. - The constructor checks whether the file was opened successfully.
- The destructor closes the stream when the object leaves scope.
- Copying is explicitly deleted because two independent objects should not pretend to own the same stream resource.
get()provides controlled access to the stream.
Although fstream already closes its file in its own destructor, this example demonstrates lifetime-based resource management. In a class owning a copyable resource, the copy constructor would need to create an independent logical resource or define clear sharing semantics. Constructors, destructors, and copying behavior must collectively maintain one consistent ownership policy.
Define a file and explain how files are opened and closed in C++ using file streams.
A file is a named collection of data stored permanently on a secondary storage device. Unlike variables in memory, file data remains available after a program terminates.
C++ provides the following stream classes in the <fstream> header:
ifstream: Opens a file for input or reading.ofstream: Opens a file for output or writing.fstream: Opens a file for both input and output.
A file can be opened through a constructor:
ifstream inputFile("data.txt");
ofstream outputFile("result.txt");
It can also be opened using the open() function:
fstream file;
file.open("data.txt", ios::in | ios::out);
The file should be checked after opening:
if (!file.is_open()) {
cout << "Unable to open file";
}
The close() function disconnects the stream from the file:
file.close();
Closing a file ensures that buffered output is written, system resources are released, and the stream can be associated with another file.
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 →