Unit 6: Handling Exceptions, Templates and STL - Subjective Questions
CAP455 — Object Oriented Programming Using C++ • Practice Questions with Detailed Answers
20 questions
Define an exception in C++. Why is exception handling preferred over traditional error-handling techniques?
Exception is an abnormal condition or runtime error that interrupts the normal flow of a program. Examples include division by zero, invalid array access, failure to open a file, and memory allocation failure.
C++ handles exceptions using three keywords:
try: Encloses code that may produce an exception.throw: Signals that an exceptional condition has occurred.catch: Handles the thrown exception.
Advantages over traditional error handling:
- It separates error-handling code from normal program logic.
- An exception can travel automatically through the function-call stack.
- Different exception types can be handled by different handlers.
- Constructors and overloaded operators can report errors effectively.
- It avoids repeated checking of error codes after every function call.
Thus, exception handling improves the clarity, reliability, and maintainability of a program.
Explain the exception-handling mechanism in C++ with a suitable example.
The C++ exception-handling mechanism consists of detecting, throwing, and catching an exception.
Working mechanism:
- Code that may cause an error is placed inside a
tryblock. - When an error is detected, an exception is generated using
throw. - The remaining statements in the
tryblock are skipped. - The runtime system searches for a compatible
catchblock. - The matching handler processes the exception.
- Execution continues after the entire
try-catchstructure.
Example:
#include <iostream>
using namespace std;
int main() {
int a = 10, b = 0;
try {
if (b == 0)
throw b;
cout << a / b;
}
catch (int value) {
cout << "Division by zero is not allowed";
}
cout << "\nProgram continues";
return 0;
}Here, throw b transfers control to catch(int value). Therefore, the invalid division is prevented and the program terminates normally.
Describe the throwing exception mechanism in C++. What kinds of values or objects can be thrown?
An exception is raised using the throw expression. Its general syntax is:
throw expression;The type of expression determines which catch handler can process the exception.
C++ permits throwing:
- Fundamental values such as
int,double, andchar - Strings
- Pointers
- Objects of user-defined classes
- Standard exception objects such as
std::runtime_error
Example using a standard exception:
#include <stdexcept>
if (age < 0) {
throw std::invalid_argument("Age cannot be negative");
}Important points:
- Statements after
throwin the current block are not executed. - A thrown object is normally copied or moved into exception storage.
- It is preferable to throw descriptive class objects rather than arbitrary numeric error codes.
- A matching handler must exist; otherwise,
std::terminate()is called.
Using standard or custom exception classes allows additional error information to be carried to the handler.
Explain how exceptions are caught in C++. Discuss multiple catch blocks and the catch-all handler.
An exception is handled by a catch block associated with a try block. Its syntax is:
try {
// Risky code
}
catch (ExceptionType parameter) {
// Handler
}A try block can have multiple handlers:
try {
// Code that may throw
}
catch (int value) {
// Handles int exceptions
}
catch (const std::runtime_error& error) {
// Handles runtime_error objects
}
catch (...) {
// Handles any remaining exception
}Rules and observations:
- Handlers are examined in the order in which they are written.
- The first compatible handler is selected.
- Only one handler executes for a thrown exception.
- The catch-all handler,
catch (...), should be placed last. - Class-type exceptions should generally be caught by
constreference to avoid copying and object slicing. - A handler for a derived exception should appear before a handler for its base class.
Multiple handlers enable different recovery actions for different categories of errors.
What is stack unwinding? Explain its role during exception propagation.
Stack unwinding is the process of removing active function-call frames from the stack while searching for a handler that can catch a thrown exception.
Suppose function main() calls f1(), which calls f2(). If f2() throws an exception and does not handle it:
- Execution of
f2()stops. - Local automatic objects in
f2()are destroyed. - Control returns exceptionally to
f1(). - If
f1()has no matching handler, its local objects are destroyed as well. - The search continues until a matching handler is found.
Importance:
- Destructors of fully constructed local objects are called automatically.
- Resources managed by RAII objects, such as file wrappers and smart pointers, are released safely.
- It permits an exception to be handled at a higher level of the program.
If no matching handler is found, the runtime calls std::terminate(). Destructors should normally not allow exceptions to escape during stack unwinding because a second active exception can also cause program termination.
What is re-throwing an exception? Explain its syntax, purpose, and behavior with an example.
Re-throwing means passing a currently handled exception to an outer handler. It is performed by writing throw; without an operand inside a catch block.
#include <iostream>
#include <stdexcept>
using namespace std;
void process() {
try {
throw runtime_error("Processing failed");
}
catch (const exception& error) {
cout << "Logging: " << error.what() << '\n';
throw;
}
}
int main() {
try {
process();
}
catch (const exception& error) {
cout << "Recovered in main: " << error.what();
}
}Purpose of re-throwing:
- A lower-level function can log the problem or perform partial cleanup.
- A higher-level component can make the final recovery decision.
- The original exception type and associated information are preserved.
Using throw; differs from throw error;. The latter throws a new copy based on the expression and may cause object slicing if error has a base-class type. A bare throw; must be used while an exception is being handled; otherwise, program termination occurs.
Differentiate between exception handling and error-code-based handling in C++.
| Basis | Exception handling | Error-code handling |
|---|---|---|
| Error reporting | Uses throw |
Returns a status value or sets a flag |
| Error processing | Uses matching catch handlers |
Uses if or switch statements |
| Control flow | Automatically propagates through function calls | Must be propagated manually by each function |
| Normal result | Function can use its return value for the actual result | Return value may be reserved for an error code |
| Separation | Separates normal logic from error logic | Often mixes error checks with normal logic |
| Constructors | Can report construction failure | Constructors cannot return error codes |
| Type information | Different exception classes represent different errors | Numeric codes may provide limited information |
| Cost | Usually low when no exception occurs, but throwing is relatively expensive | Every call may require explicit checking |
Exceptions are suitable for exceptional failures that cannot be handled locally. Error codes can still be appropriate for routine, expected outcomes where callers are intended to test the result directly.
Define a function template. Write and explain a C++ function template that returns the larger of two values.
A function template is a generic blueprint from which the compiler creates type-specific functions. It allows the same algorithm to operate on values of different data types.
#include <iostream>
#include <string>
using namespace std;
template <typename T>
T larger(const T& a, const T& b) {
return (a > b) ? a : b;
}
int main() {
cout << larger(10, 20) << '\n';
cout << larger(4.5, 2.1) << '\n';
cout << larger(string("cat"), string("apple"));
}Explanation:
template <typename T>declaresTas a template type parameter.- Both parameters must be compatible with type
T. - The type must support the
>operator. larger(10, 20)causes the compiler to instantiatelarger<int>.larger(4.5, 2.1)instantiateslarger<double>.
class can be used instead of typename in this template parameter declaration. Function templates reduce duplication while retaining compile-time type checking.
Explain template argument deduction, explicit template arguments, and function template overloading.
Template argument deduction allows the compiler to determine template arguments from function arguments.
template <typename T>
T square(T value) {
return value * value;
}
square(5); // T is deduced as int
square(2.5); // T is deduced as doubleA programmer may supply an explicit template argument:
square<double>(5);Here, T is explicitly selected as double, so 5 is converted accordingly.
Function templates can also be overloaded:
template <typename T>
void display(const T& value) { }
template <typename T>
void display(const T* value) { }
void display(int value) { }Selection principles:
- The compiler considers ordinary functions and generated template specializations.
- A non-template overload may be preferred when it provides an equally good match.
- Among templates, the more specialized applicable template is generally chosen.
- Deduction can fail when arguments imply conflicting types, such as a single-type
maximum(2, 3.5).
Explicit arguments or a template with separate parameter types can resolve such conflicts.
Define a class template. Design a generic Pair class and explain how objects of different specializations are created.
A class template defines a family of classes parameterized by one or more types or values.
#include <iostream>
#include <string>
using namespace std;
template <typename T1, typename T2>
class Pair {
private:
T1 first;
T2 second;
public:
Pair(const T1& a, const T2& b) : first(a), second(b) { }
T1 getFirst() const {
return first;
}
T2 getSecond() const {
return second;
}
};
int main() {
Pair<int, double> p1(5, 8.5);
Pair<string, int> p2("Age", 20);
cout << p1.getFirst() << ' ' << p1.getSecond();
}Explanation:
T1andT2are independent template parameters.Pair<int, double>andPair<string, int>are different class specializations.- The compiler generates the required class definitions during compilation.
- Member operations must be valid for the substituted types.
Class templates provide reusable, type-safe data structures and form the basis of STL containers such as vector<T> and list<T>.
Explain class template specialization. Distinguish between full specialization and partial specialization with examples.
Template specialization provides a customized implementation for particular template arguments.
Primary template:
template <typename T>
class Printer {
public:
void print(const T&) {
// General implementation
}
};Full specialization: It supplies an implementation for one exact template argument.
template <>
class Printer<bool> {
public:
void print(bool value) {
std::cout << (value ? "true" : "false");
}
};This version is used specifically for Printer<bool>.
Partial specialization: It customizes a category of class-template arguments.
template <typename T>
class Printer<T*> {
public:
void print(T* pointer) {
std::cout << *pointer;
}
};This version applies to pointer types such as Printer<int*> and Printer<double*>.
Difference:
- Full specialization fixes all template parameters.
- Partial specialization fixes or restricts only some aspect of the parameters.
- Class templates support both full and partial specialization.
- Function templates can be fully specialized, but they cannot be partially specialized; overloading is generally used instead.
Discuss the possible relationships between class templates and inheritance in C++. Illustrate template-to-template inheritance with a program.
Templates and inheritance can be combined in several ways:
- A non-template class can derive from a specialization of a class template.
- A class template can derive from a non-template base class.
- A class template can derive from another class template.
- A derived template may pass its own parameter to the base template.
Template-to-template inheritance:
#include <iostream>
using namespace std;
template <typename T>
class Storage {
protected:
T value;
public:
explicit Storage(const T& value) : value(value) { }
};
template <typename T>
class DisplayStorage : public Storage<T> {
public:
explicit DisplayStorage(const T& value) : Storage<T>(value) { }
void display() const {
cout << this->value;
}
};
int main() {
DisplayStorage<int> object(25);
object.display();
}DisplayStorage<T> inherits from Storage<T>. The expression Storage<T>(value) invokes the base constructor.
Because Storage<T> is a dependent base class, the derived template uses this->value to make the inherited member lookup dependent on T. Inheritance enables generic interfaces and reusable implementations, but public inheritance should still represent a valid is-a relationship.
What is the Standard Template Library (STL)? Explain its importance and major components.
The Standard Template Library (STL) is a major part of the C++ Standard Library that provides generic data structures and algorithms. It is based on templates, so the same components can work with many data types.
Major components:
- Containers store collections of objects. Examples include
vector,list,deque,set, andmap. - Algorithms perform operations such as sorting, searching, counting, copying, and reversing.
- Iterators provide a common interface for traversing container elements.
- Function objects and callable objects customize the behavior of algorithms.
- Adapters modify existing interfaces, as in
stack,queue, andpriority_queue. - Allocators manage storage used by containers.
Importance of STL:
- Reduces development time by providing tested components.
- Encourages reusable and generic programming.
- Provides efficient implementations with documented complexity.
- Ensures portability across standard-compliant compilers.
- Allows algorithms and containers to interact through iterators.
- Improves type safety compared with untyped data structures.
For example, std::sort(v.begin(), v.end()) sorts a vector without requiring a vector-specific sorting function.
Classify STL containers and give suitable examples of each category.
STL containers can be classified into the following categories:
1. Sequence containers
They arrange elements in a linear sequence.
array: Fixed-size contiguous collectionvector: Dynamic contiguous arraydeque: Double-ended sequencelist: Doubly linked listforward_list: Singly linked list
2. Associative containers
They maintain elements in sorted order, generally using tree-based structures.
setandmultisetmapandmultimap
A map stores key-value pairs, while a set stores keys.
3. Unordered associative containers
They use hashing and do not maintain sorted order.
unordered_setandunordered_multisetunordered_mapandunordered_multimap
They provide average constant-time lookup when hashing behaves well.
4. Container adapters
They provide restricted interfaces over underlying containers.
stack: Last-in, first-out behaviorqueue: First-in, first-out behaviorpriority_queue: Access to the highest-priority element
Container selection depends on required operations, ordering, iterator stability, lookup speed, and memory characteristics.
Explain STL algorithms. Demonstrate the use of sorting, searching, counting, and transformation algorithms on a vector.
STL algorithms are generic functions that operate primarily on ranges specified by iterators. Most are available through the <algorithm> header.
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> values{5, 2, 8, 2, 1};
sort(values.begin(), values.end());
bool present = binary_search(values.begin(), values.end(), 5);
int occurrences = count(values.begin(), values.end(), 2);
transform(values.begin(), values.end(), values.begin(),
[](int value) { return value * value; });
for (int value : values)
cout << value << ' ';
}Explanation:
sortarranges the elements in ascending order.binary_searchchecks for an element in a sorted range.countreturns the number of matching elements.transformapplies a callable to every element and stores the results.- The range
[begin, end)includes the element atbeginbut excludesend.
Algorithms are independent of specific containers when the supplied iterators satisfy their requirements. For instance, sort requires random-access iterators and therefore works with vector, but not directly with list.
Define an iterator. Explain the major categories of STL iterators and their capabilities.
An iterator is an object that identifies a position in a sequence and provides pointer-like operations for accessing and traversing elements. Iterators connect STL containers with generic algorithms.
Major iterator categories:
-
Input iterator
- Reads elements while moving forward.
- Commonly supports
*itand++it.
-
Output iterator
- Writes elements while moving forward.
- Used for destinations of operations such as copying.
-
Forward iterator
- Supports repeated forward traversal.
- Can read or write depending on constness.
-
Bidirectional iterator
- Supports both
++itand--it. - Provided by containers such as
listandset.
- Supports both
-
Random-access iterator
- Supports arithmetic such as
it + n, subtraction, and indexing. - Provided by
vector,deque, andarray.
- Supports arithmetic such as
-
Contiguous iterator
- A random-access iterator whose elements occupy contiguous memory.
- Associated with containers such as
vectorandarray.
Stronger categories include the operations of weaker relevant categories. Algorithm requirements must be matched with the iterator capabilities supplied by a container.
Distinguish between iterator, const_iterator, and reverse_iterator. Explain iterator invalidation.
Iterator types:
iterator: Provides access to an element and usually permits modification.const_iterator: Provides read-only access; the referenced element cannot be modified through it.reverse_iterator: Traverses a sequence in reverse order, normally fromrbegin()torend().
std::vector<int> values{10, 20, 30};
for (std::vector<int>::iterator it = values.begin(); it != values.end(); ++it)
*it += 1;
for (std::vector<int>::const_iterator it = values.cbegin(); it != values.cend(); ++it)
std::cout << *it << ' ';
for (auto it = values.rbegin(); it != values.rend(); ++it)
std::cout << *it << ' ';Iterator invalidation occurs when a container operation makes an existing iterator, pointer, or reference unsafe to use.
- Reallocation of a
vectorinvalidates all iterators, pointers, and references to its elements. - Erasing a vector element invalidates iterators at and after the erased position.
- In a
list, insertion normally does not invalidate existing iterators. - Erasing a list element invalidates only iterators and references to that element.
Programs must reacquire invalidated iterators before further use.
Describe the vector container in detail. Discuss its important operations, size, capacity, and performance characteristics.
std::vector is a sequence container that stores elements in contiguous memory and can change its size dynamically.
Important operations:
push_back(value): Appends an element.pop_back(): Removes the last element.insert(position, value): Inserts at a specified position.erase(position): Removes an element.at(index): Performs bounds-checked access.operator[]: Performs unchecked indexed access.front()andback(): Access the first and last elements.clear(): Removes all elements.
Size and capacity:
size()is the number of stored elements.capacity()is the number of elements that can be stored before allocation is required.reserve(n)requests capacity for at leastnelements without changing the size.resize(n)changes the number of elements.
Performance:
- Indexed access: constant time,
push_back: amortized- Insertion or deletion near the middle:
- Search in an unsorted vector:
A vector is appropriate when fast indexing, cache efficiency, and frequent insertion at the end are important.
Describe the list container in C++. Explain its operations, advantages, limitations, and iterator behavior.
std::list is a sequence container typically implemented as a doubly linked list. Its elements are stored in separate nodes connected by links.
Important operations:
push_front()andpush_back()pop_front()andpop_back()insert()anderase()remove(value)sort()reverse()merge()splice()
Advantages:
- Insertion and erasure at a known position are constant time, .
- Existing iterators generally remain valid after insertion.
- Erasure invalidates only iterators and references to erased elements.
splice()can transfer nodes between lists efficiently.
Limitations:
- It does not support indexed access such as
items[3]. - Reaching the th element takes time.
- Each node requires extra memory for links.
- Non-contiguous storage generally provides poorer cache locality than a vector.
- It provides bidirectional rather than random-access iterators.
Because std::sort requires random-access iterators, a list is sorted using its member function: items.sort().
Compare the vector and list containers. How should a programmer select between them for a given application?
| Feature | vector |
list |
|---|---|---|
| Internal organization | Contiguous dynamic array | Doubly linked nodes |
| Indexed access | Supported in | Not supported; traversal is |
| End insertion | Amortized | |
| Known-position insertion | Usually due to movement | once the position is known |
| Known-position erasure | due to movement | |
| Iterator type | Random-access and contiguous | Bidirectional |
| Memory overhead | Usually low per element | Extra link storage per node |
| Cache locality | High | Generally lower |
| Reallocation | May invalidate all iterators | Does not occur in the same manner |
| Sorting | std::sort |
Member function sort() |
Selection guidelines:
- Choose
vectorby default when fast indexed access, compact storage, traversal speed, or end insertion is required. - Choose
listwhen the program frequently inserts, erases, or transfers nodes at already known positions and requires stable iterators. - A list is not automatically faster for frequent insertion if locating the insertion position itself requires linear traversal.
- Actual operation patterns, memory usage, and cache behavior should guide the final decision.
Define an exception in C++. Why is exception handling preferred over traditional error-handling techniques?
Exception is an abnormal condition or runtime error that interrupts the normal flow of a program. Examples include division by zero, invalid array access, failure to open a file, and memory allocation failure.
C++ handles exceptions using three keywords:
try: Encloses code that may produce an exception.throw: Signals that an exceptional condition has occurred.catch: Handles the thrown exception.
Advantages over traditional error handling:
- It separates error-handling code from normal program logic.
- An exception can travel automatically through the function-call stack.
- Different exception types can be handled by different handlers.
- Constructors and overloaded operators can report errors effectively.
- It avoids repeated checking of error codes after every function call.
Thus, exception handling improves the clarity, reliability, and maintainability of a program.
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 →