Week 8 - Gemini Flashcards
(30 cards)
When designing a Matrix class that stores its data in a dynamically allocated 1D array (double* data
), how can the element at (row_i, col_j)
be accessed if nRows
is the number of rows?
The element (i, j)
can be accessed at data[j * nRows + i]
(column-major) or data[i * nCols + j]
(row-major, assuming nCols
is number of columns). The lecture example uses data[j * nRows + i]
, implying column-major storage for the 1D representation.
What is ‘const overloading’ of member functions, as seen in the Matrix class example with begin()
and end()
methods?
Providing two versions of a member function with the same name and parameters, but one is a const
member function (e.g., const double* begin() const
) and the other is non-const
(e.g., double* begin()
). The const
version is called on const
objects and returns a pointer/reference to const
, while the non-const
version is called on non-const
objects and allows modification.
What is the ‘Rule of Three’ in C++ class design, especially when a class manages dynamic memory (like the Matrix class)?
If a class has a non-trivial destructor (e.g., it deallocates dynamic memory), it usually needs to explicitly define: 1. The destructor. 2. The copy constructor. 3. The copy assignment operator (operator=
). If these are not user-defined, the compiler’s default versions might lead to issues like shallow copies and double frees with dynamic memory.
When overloading operator+=
for a Matrix class as a member function, why should it typically return a reference to *this
?
Returning Matrix&
(specifically *this
) allows for chaining of assignments, like (a += b) += c;
, which is conventional for assignment-like operators.
How can the function call operator operator()
be overloaded for a Matrix class? What does it enable?
It can be overloaded as a member function, e.g., double& operator()(int r, int c);
and const double& operator()(int r, int c) const;
. This enables accessing/modifying matrix elements using function-call-like syntax, e.g., matrix(row, col) = value;
.
What is the purpose of the std::stringstream
class, and how is it typically used for building strings efficiently? Which header is needed?
std::stringstream
allows strings to be built by inserting data into it using <<
(like cout
). It’s more efficient than repeated string concatenation with +
. After all data is inserted, the complete string is retrieved using the .str()
method. Header: <sstream>
.
What is ‘inheritance’ in Object-Oriented Programming? Define base class (superclass) and derived class (subclass).
Inheritance is a mechanism where a new class (derived class or subclass) inherits properties (data members) and behaviors (member functions) from an existing class (base class or superclass). It establishes an “is-a” relationship.
What is the syntax for declaring a class Derived
that publicly inherits from a class Base
?
class Derived : public Base { /* ... members of Derived ... */ };
What does public
inheritance mean in terms of accessibility of base class members in the derived class?
public
members of the base class remain public
in the derived class. protected
members of the base class remain protected
in the derived class. private
members of the base class are inherited but are not directly accessible by the derived class’s member functions.
What is the protected
access specifier used for in a base class?
protected
members are accessible within the base class itself and by member functions of any classes directly or indirectly derived from it. They are not accessible by unrelated classes or code outside the class hierarchy.
In what order are constructors called when an object of a derived class is created?
The base class constructor is called first, followed by the derived class constructor. If there’s a multi-level hierarchy, constructors are called from the topmost base class downwards.
In what order are destructors called when an object of a derived class is destroyed?
The derived class destructor is called first, followed by the base class destructor (the reverse order of constructor calls).
What is ‘function overriding’ in the context of inheritance? What must be true about the function signatures?
Function overriding occurs when a derived class provides a specific implementation for a member function that is already defined in its base class. The function in the derived class must have an identical signature (name, parameter types, and const
-ness) to the base class function.
How can a derived class explicitly call an overridden member function from its immediate base class Base
?
Using the scope resolution operator: Base::functionName(arguments);
.
Distinguish between inheritance (‘is-a’ relationship) and composition (‘has-a’ relationship) in class design.
Inheritance implies that the derived class is a specialized type of the base class (e.g., a SavingsAccount
is an Account
). Composition means a class has a member object of another class type (e.g., a Car
has an Engine
). Choose inheritance for specialization and polymorphism; choose composition for reusing functionality by embedding.
What is ‘polymorphism’ in C++? What does it allow you to do?
Polymorphism (meaning “many forms”) allows objects of different derived classes to be treated through a common base class interface, typically using base class pointers or references. The same function call can behave differently depending on the actual runtime type of the object.
What is a virtual
function in a base class, and how does it enable polymorphism?
A virtual
function is a member function declared with the virtual
keyword in a base class. It signals that derived classes might override it. When a virtual
function is called through a base class pointer or reference, the call is resolved at runtime (late binding) to the version of the function in the actual type of the object, enabling polymorphic behavior.
Explain ‘late binding’ (dynamic dispatch) versus ‘early binding’ (static dispatch).
Early binding: The function call is resolved at compile time based on the static type of the pointer/reference. This is the default for non-virtual functions. Late binding: The function call is resolved at runtime based on the actual dynamic type of the object being pointed/referred to. This occurs for virtual
functions called via base class pointers/references.
What is ‘upcasting’? Is it generally safe?
Upcasting is the act of treating a pointer or reference to a derived class object as a pointer or reference to its base class type (e.g., Base* b_ptr = &derived_obj;
). It is generally safe because a derived class object “is-a” base class object and contains all its members.
What is ‘object slicing’? When does it occur, and why is it problematic for polymorphism?
Object slicing occurs when a derived class object is assigned or copied by value to a base class object. Only the base class part of the derived object is copied, and the derived-specific parts are ‘sliced off’. If virtual functions are then called on this base class object, they will resolve to the base class versions, losing polymorphic behavior.
How do you achieve polymorphic behavior correctly, avoiding object slicing?
By using pointers or references to the base class to refer to derived class objects.
What is a ‘pure virtual function’? What is its syntax, and what effect does it have on a class?
A pure virtual function is a virtual function in a base class for which the base class provides no implementation. Syntax: virtual ReturnType functionName(parameters) = 0;
. Its effect is that it makes the class an ‘abstract base class’.
What is an ‘abstract base class’ (ABC)? Can you create instances of an ABC?
An ABC is a class that has at least one pure virtual function. You cannot create direct instances (objects) of an ABC. It’s meant to be used as a base class for other classes to derive from.
What is an ‘interface class’ (or pure abstract class)?
A class that contains only pure virtual functions (and typically a virtual destructor) and no data members. It defines a contract or interface that derived classes must implement.