CPP in Practice - Developer's Blog
Technical blog about C++!
28/02/2021
đź’¬
27/02/2021
âś…The override keyword was introduced in C++11. Keyword override specifies that a virtual function overrides another virtual function. If something will be wrong in derived class virtual function compile-time error will be generated.
âś…For overriding to occur, several requirements must be met:
• The base class function must be virtual.
• The base and derived function names must be identical (except in case of destructors).
• The parameter types of the base and derived functions must be identical.
• The constness of the base and derived functions must be identical.
• The return types and exceptions specifications of the base and derived functions must be compatible.
• The function’s reference qualifiers must be identical.
✅So, why it is preferred to declare overriding functions as override? We can forget mark overriding function as const, pass unsigned int as parameter instead of int, or declare function as noexcept and etc… In general, the previous examples didn’t wrong. This is because we can can declare virtual function with int parameter in base class, but derived class function may accept only unsigned value and it’s expected.
ℹ Let’s discuss the following example:
class base
{
public:
virtual void f(int);
virtual void g() const;
};
class derived : public base
{
public:
virtual void f(bool);
virtual void g();
};
derived* d = new derived;
d->f(0);
d->g();
✅The code will be compiled and will work. But what if we mark derived class functions as override? In this case we can’t compile source code. This is because:
• derived::f() – parameter type mismatch. f() gets the bool parameter instead of int.
• derived::g() – specifier mismatch. g() in derived class is not const.
ℹ If we would like override derived class functions it will look like as following:
class derived : public base
{
public:
virtual void f(int) override;
void g() const override;
};
📌Note: If we are overriding function, adding “virtual” keyword is OK, but not necessary.
âś…So, if we are overriding functions it is preferred to mark it as override (to avoid mistakes). But if we would like to create another virtual function in derived class which will have same name as in base class function too, in this case writing override keyword is wrong usage in this context.
📌In case of virtual function overloading that functions are not visible in derived class and if we need them in derived class we should call explicitly (Ex: call base::f()).
đź“–Read more about override specifier - https://en.cppreference.com/w/cpp/language/override
đź“–override keyword usage and advices - Scott Meyers, Effective Modern C++, Item 12: Declare overriding functions override.
âś…Values known during compilation are privileged, right? The keyword constexpr was introduced in C++11 and improved in C++14 which allows to define an expression that can be evaluated at compile time. They may be placed in read-only memory.
📌All constexpr objects are const, but not all const objects are constexpr. This is because const object’s value may be not known at compilation.
int i;
…
const auto k = i; /// OK
constexpr int m = 0; /// OK
constexpr int n = i; /// Error
✅constexpr is part of an object’s or function’s interface. In case when we have objects whose values are known during compilation we can define functions as constexpr too.
Small example:
constexpr int max_count()
{
return 100;
}
âś…constexpr functions are limited to taking and returning literal types, which essentially means types that can have values determined during compilation. The user-defined types also can be literal, too, because we can declare constructors and member functions as constexpr.
class Pair
{
public:
constexpr Pair(double f = 0.0, double s = 0.0) noexcept
: first(f), second(s) {}
constexpr double get_first() const noexcept { return first; }
constexpr double get_second() const noexcept { return second; }
void set_first(double f) noexcept { first = f; }
void set_second(double s) noexcept { second = s; }
private:
double first;
double second;
};
âś…In this case we can create an object of Pair type as constexpr.
constexpr Pair p1(1.0, -1.0); /// OK
Pair p2; /// OK
âś…Now, when we have constexpr getter functions in Pair, we can use them inside of constexpr functions. Example:
constexpr bool less(const Pair& p1, const Pair& p2) noexcept
{
if (p1.get_first() < p2.get_first()) {
return true;
} else if (p1.get_first() == p2.get_first()) {
return p1.get_second() < p2.get_second();
}
return false;
}
…
less(p1, p2); /// OK, because values of p2 are known during compilation
âś…So, both constexpr objects and constexpr functions can be employed in a wider range of contexts than non-constexpr objects and functions. This is why declaration constexpr objects and functions preferred whenever it is possible.
📚Read more about constexpr specifier - https://en.cppreference.com/w/cpp/language/constexpr
📚constexpr keyword usage and advices - Scott Meyers, Effective Modern C++, Item 15: Use constexpr whenever possible
âś…The reference qualifiers can have only class member functions. Also, called by ref-qualifiers. Ref-qualifiers are new in C++11 and not yet supported in all compilers. They are less-publicized features and maybe you have never heard about them. Reference qualifiers are using to specify that the mentioned function can be called only for lvalue (&) or rvalue (&&) objects.
class Base
{
public:
static Base create() {
return Base();
}
void f() & { … }
void g() && { … }
void h() & { … } /// for lvalue objects
void h() && { … } /// for rvalue objects
};
int main() {
...
Base b;
b.f(); /// OK
b.g(); /// Compile error
…
b.h(); /// OK
Base::create().h(); /// Compile error
}
✅So, we will get compile error if we call g() && and h() && functions carelessly. This is simple case. But, what if f() and g() are virtual? In general, nothing dangerous. But… There are still but. We can mark it as override also in derived class, right? We will speak about override keyword and function signature later, but for now, let’s declare Base and Derived classes:
class Base
{
…
virtual void foo() & { … }
virtual void bar() && { … }
};
class Derived : public base
{
…
virtual void foo() & override { … } /// It’s OK to add “virtual”, but not necessary
virtual void bar() & { … }
};
✅Will Derived::bar() override Base::bar() ?? NO! In virtual table will be created second function pointer named bar. This is because ref-qualifiers also part of function’s signature. This is also good example why we need to write override keyword if we are overriding functions.
📌BTW, about static member functions also can’t have ref-qualifiers.
📚Read more about non-static member functions - https://en.cppreference.com/w/cpp/language/member_functions
📚Read more about lvalue/rvalue references - https://en.cppreference.com/w/cpp/language/reference
05/02/2021
ℹ️Role of virtual destructors
❓We can see destructors declared as virtual very often and in some cases after some investigations we can see that destructor should not be virtual. But why? When exactly do we need to declare virtual destructors?
📌Consider, we have class Base and Derived(which is derived from Base) and Pair structure which is not designed to be base for other classes.
class Base
{
public:
Base() { std::cout
ℹ Do you ever think about functions that can't return even if their return type declared void and doesn't return anything?
Example:
inline void f()
{
std::cout
C was chosen as the base language for C++ because it
[1] is versatile, terse, and relatively low-level;
[2] is adequate for most systems programming tasks;
[3] runs everywhere and on everything; and
[4] fits into the UNIX programming environment.
- Bjarne Stroustrup, The C++ Programming Language Third Edition
Hello!
Here you can find interesting and useful posts about C++.
Join us!
Click here to claim your Sponsored Listing.
Category
Website
Address
Yerevan