C++ 14 Inheritance
C++ 14 Inheritance
Article • 08/03/2021
This section explains how to use derived classes to produce extensible programs.
Overview
New classes can be derived from existing classes using a mechanism called "inheritance"
(see the information beginning in Single Inheritance). Classes that are used for
derivation are called "base classes" of a particular derived class. A derived class is
declared using the following syntax:
C++
After the tag (name) for the class, a colon appears followed by a list of base
specifications. The base classes so named must have been declared previously. The base
specifications may contain an access specifier, which is one of the keywords public ,
protected or private . These access specifiers appear before the base class name and
apply only to that base class. These specifiers control the derived class's permission to
use to members of the base class. See Member-Access Control for information on
access to base class members. If the access specifier is omitted, the access to that base
is considered private . The base specifications may contain the keyword virtual to
indicate virtual inheritance. This keyword may appear before or after the access specifier,
if any. If virtual inheritance is used, the base class is referred to as a virtual base class.
Multiple base classes can be specified, separated by commas. If a single base class is
specified, the inheritance model is Single inheritance. If more than one base class is
specified, the inheritance model is called Multiple inheritance.
Single inheritance
Multiple base classes
Virtual functions
Explicit overrides
Abstract classes
See also
C++ Language Reference
Virtual Functions
Article • 04/07/2022
Virtual functions ensure that the correct function is called for an object, regardless of the
expression used to make the function call.
Suppose a base class contains a function declared as virtual and a derived class defines
the same function. The function from the derived class is invoked for objects of the
derived class, even if it is called using a pointer or reference to the base class. The
following example shows a base class that provides an implementation of the
PrintBalance function and two derived classes
C++
// deriv_VirtualFunctions.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
class Account {
public:
Account( double d ) { _balance = d; }
virtual ~Account() {}
virtual double GetBalance() { return _balance; }
virtual void PrintBalance() { cerr << "Error. Balance not available for
base type." << endl; }
private:
double _balance;
};
In the preceding code, the calls to PrintBalance are identical, except for the object
pAccount points to. Because PrintBalance is virtual, the version of the function defined
for each object is called. The PrintBalance function in the derived classes
CheckingAccount and SavingsAccount "override" the function in the base class Account .
Functions in derived classes override virtual functions in base classes only if their type is
the same. A function in a derived class cannot differ from a virtual function in a base
class in its return type only; the argument list must differ as well.
When calling a function using pointers or references, the following rules apply:
A call to a virtual function is resolved according to the underlying type of object for
which it is called.
The following example shows how virtual and nonvirtual functions behave when called
through pointers:
C++
// deriv_VirtualFunctions2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
class Base {
public:
virtual void NameOf(); // Virtual function.
void InvokingClass(); // Nonvirtual function.
};
void Base::InvokingClass() {
cout << "Invoked by Base\n";
}
void Derived::InvokingClass() {
cout << "Invoked by Derived\n";
}
int main() {
// Declare an object of type Derived.
Derived aDerived;
Output
Derived::NameOf
Invoked by Base
Derived::NameOf
Invoked by Derived
Note that regardless of whether the NameOf function is invoked through a pointer to
Base or a pointer to Derived , it calls the function for Derived . It calls the function for
Derived because NameOf is a virtual function, and both pBase and pDerived point to an
Because virtual functions are called only for objects of class types, you cannot declare
global or static functions as virtual .
The virtual keyword can be used when declaring overriding functions in a derived
class, but it is unnecessary; overrides of virtual functions are always virtual.
Virtual functions in a base class must be defined unless they are declared using the
pure-specifier. (For more information about pure virtual functions, see Abstract Classes.)
C++
Both calls to PrintBalance in the preceding example suppress the virtual function-call
mechanism.
Single Inheritance
Article • 11/11/2021
In "single inheritance," a common form of inheritance, classes have only one base class.
Consider the relationship illustrated in the following figure.
Note the progression from general to specific in the figure. Another common attribute
found in the design of most class hierarchies is that the derived class has a "kind of"
relationship with the base class. In the figure, a Book is a kind of a PrintedDocument , and
a PaperbackBook is a kind of a book .
One other item of note in the figure: Book is both a derived class (from
PrintedDocument ) and a base class ( PaperbackBook is derived from Book ). A skeletal
C++
// deriv_SingleInheritance.cpp
// compile with: /LD
class PrintedDocument {};
to PaperbackBook . The difference is that a direct base class appears in the base list of a
class declaration and an indirect base does not.
The base class from which each class is derived is declared before the declaration of the
derived class. It is not sufficient to provide a forward-referencing declaration for a base
class; it must be a complete declaration.
In the preceding example, the access specifier public is used. The meaning of public,
protected, and private inheritance is described in Member-Access Control.
A class can serve as the base class for many specific classes, as illustrated in the
following figure.
In the diagram shown above, called a "directed acyclic graph" (or "DAG"), some of the
classes are base classes for more than one derived class. However, the reverse is not
true: there is only one direct base class for any given derived class. The graph in the
figure depicts a "single inheritance" structure.
7 Note
Directed acyclic graphs are not unique to single inheritance. They are also used to
depict multiple-inheritance graphs.
In inheritance, the derived class contains the members of the base class plus any new
members you add. As a result, a derived class can refer to members of the base class
(unless those members are redefined in the derived class). The scope-resolution
operator ( :: ) can be used to refer to members of direct or indirect base classes when
those members have been redefined in the derived class. Consider this example:
C++
// deriv_SingleInheritance2.cpp
// compile with: /EHsc /c
#include <iostream>
using namespace std;
class Document {
public:
char *Name; // Document name.
void PrintNameOf(); // Print name.
};
Note that the constructor for Book , ( Book::Book ), has access to the data member, Name .
In a program, an object of type Book can be created and used as follows:
C++
...
C++
// deriv_SingleInheritance3.cpp
// compile with: /EHsc /LD
#include <iostream>
using namespace std;
class Document {
public:
char *Name; // Document name.
void PrintNameOf() {} // Print name.
};
Pointers and references to derived classes can be implicitly converted to pointers and
references to their base classes if there is an accessible, unambiguous base class. The
following code demonstrates this concept using pointers (the same principle applies to
references):
C++
// deriv_SingleInheritance4.cpp
// compile with: /W3
struct Document {
char *Name;
void PrintNameOf() {}
};
int main() {
Document * DocLib[10]; // Library of ten documents.
for (int i = 0 ; i < 5 ; i++)
DocLib[i] = new Document;
for (int i = 5 ; i < 10 ; i++)
DocLib[i] = new PaperbackBook;
}
In the preceding example, different types are created. However, because these types are
all derived from the Document class, there is an implicit conversion to Document * . As a
result, DocLib is a "heterogeneous list" (a list in which not all objects are of the same
type) containing different kinds of objects.
Because the Document class has a PrintNameOf function, it can print the name of each
book in the library, although it may omit some of the information specific to the type of
document (page count for Book , number of bytes for HelpFile , and so on).
7 Note
Forcing the base class to implement a function such as PrintNameOf is often not the
best design. Virtual Functions offers other design alternatives.
Base Classes
Article • 08/03/2021
The inheritance process creates a new derived class that is made up of the members of
the base class(es) plus any new members added by the derived class. In a multiple-
inheritance, it is possible to construct an inheritance graph where the same base class is
part of more than one of the derived classes. The following figure shows such a graph.
A class can be derived from more than one base class. In a multiple-inheritance model
(where classes are derived from more than one base class), the base classes are specified
using the base-list grammar element. For example, the class declaration for
CollectionOfBook , derived from Collection and Book , can be specified:
C++
// deriv_MultipleBaseClasses.cpp
// compile with: /LD
class Collection {
};
class Book {};
class CollectionOfBook : public Book, public Collection {
// New members
};
The order in which base classes are specified isn't significant except in certain cases
where constructors and destructors are invoked. In these cases, the order in which base
classes are specified affects the following:
The order in which constructors are called. If your code relies on the Book portion
of CollectionOfBook to be initialized before the Collection part, the order of
specification is significant. Initialization takes place in the order the classes are
specified in the base-list.
The order in which destructors are invoked to clean up. Again, if a particular "part"
of the class must be present when the other part is being destroyed, the order is
significant. Destructors are called in the reverse order of the classes specified in the
base-list.
7 Note
The order of specification of base classes can affect the memory layout of the
class. Do not make any programming decisions based on the order of base
members in memory.
When specifying the base-list, you can't specify the same class name more than once.
However, it's possible for a class to be an indirect base to a derived class more than
once.
Virtual base classes
Because a class can be an indirect base class to a derived class more than once, C++
provides a way to optimize the way such base classes work. Virtual base classes offer a
way to save space and avoid ambiguities in class hierarchies that use multiple
inheritance.
Each nonvirtual object contains a copy of the data members defined in the base class.
This duplication wastes space and requires you to specify which copy of the base class
members you want whenever you access them.
When a base class is specified as a virtual base, it can act as an indirect base more than
once without duplication of its data members. A single copy of its data members is
shared by all the base classes that use it as a virtual base.
When declaring a virtual base class, the virtual keyword appears in the base lists of the
derived classes.
Consider the class hierarchy in the following figure, which illustrates a simulated lunch
line:
In the figure, Queue is the base class for both CashierQueue and LunchQueue . However,
when both classes are combined to form LunchCashierQueue , the following problem
arises: the new class contains two subobjects of type Queue , one from CashierQueue and
the other from LunchQueue . The following figure shows the conceptual memory layout
(the actual memory layout might be optimized):
C++
// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};
The virtual keyword ensures that only one copy of the subobject Queue is included
(see the following figure).
A class can have both a virtual component and a nonvirtual component of a given type.
This happens in the conditions illustrated in the following figure:
In the figure, CashierQueue and LunchQueue use Queue as a virtual base class. However,
TakeoutQueue specifies Queue as a base class, not a virtual base class. Therefore,
LunchTakeoutCashierQueue has two subobjects of type Queue : one from the inheritance
path that includes LunchCashierQueue and one from the path that includes
TakeoutQueue . This is illustrated in the following figure.
Object layout with virtual and nonvirtual inheritance
7 Note
Virtual inheritance provides significant size benefits when compared with nonvirtual
inheritance. However, it can introduce extra processing overhead.
If a derived class overrides a virtual function that it inherits from a virtual base class, and
if a constructor or a destructor for the derived base class calls that function using a
pointer to the virtual base class, the compiler may introduce other hidden "vtordisp"
fields into the classes with virtual bases. The /vd0 compiler option suppresses the
addition of the hidden vtordisp constructor/destructor displacement member. The /vd1
compiler option, the default, enables them where they're necessary. Turn off vtordisps
only if you're sure that all class constructors and destructors call virtual functions
virtually.
The /vd compiler option affects an entire compilation module. Use the vtordisp
pragma to suppress and then reenable vtordisp fields on a class-by-class basis:
C++
Name ambiguities
Multiple inheritance introduces the possibility for names to be inherited along more
than one path. The class-member names along these paths aren't necessarily unique.
These name conflicts are called "ambiguities."
Any expression that refers to a class member must make an unambiguous reference.
The following example shows how ambiguities develop:
C++
// deriv_NameAmbiguities.cpp
// compile with: /LD
// Declare two base classes, A and B.
class A {
public:
unsigned a;
unsigned b();
};
class B {
public:
unsigned a(); // class A also has a member "a"
int b(); // and a member "b".
char c;
};
Given the preceding class declarations, code such as the following is ambiguous
because it's unclear whether b refers to the b in A or in B :
C++
C *pc = new C;
pc->b();
Consider the preceding example. Because the name a is a member of both class A and
class B , the compiler can't discern which a designates the function to be called. Access
to a member is ambiguous if it can refer to more than one function, object, type, or
enumerator.
C *pc = new C;
pc->B::a();
7 Note
When C is declared, it has the potential to cause errors when B is referenced in the
scope of C . No error is issued, however, until an unqualified reference to B is
actually made in C 's scope.
Dominance
it's possible for more than one name (function, object, or enumerator) to be reached
through an inheritance graph. Such cases are considered ambiguous with nonvirtual
base classes. They're also ambiguous with virtual base classes, unless one of the names
"dominates" the others.
A name dominates another name if it's defined in both classes and one class is derived
from the other. The dominant name is the name in the derived class; this name is used
when an ambiguity would otherwise have arisen, as shown in the following example:
C++
// deriv_Dominance.cpp
// compile with: /LD
class A {
public:
int a;
};
The effect of applying the address-of operator (&) to that object. The address-of
operator always supplies the base address of the object.
The effect of explicitly converting the pointer obtained using the address-of
operator to the base-class type A . Coercing the address of the object to type A*
doesn't always provide the compiler with enough information as to which
subobject of type A to select; in this case, two subobjects exist.
C++
The following figure shows how objects are composed using virtual and nonvirtual
inheritance.
Virtual and nonvirtual derivation
In the figure, accessing any member of class A through nonvirtual base classes causes
an ambiguity; the compiler has no information that explains whether to use the
subobject associated with B or the subobject associated with C . However, when A is
specified as a virtual base class, there's no question which subobject is being accessed.
See also
Inheritance
Explicit Overrides (C++)
Article • 08/03/2021
Microsoft Specific
If the same virtual function is declared in two or more interfaces and if a class is derived
from these interfaces, you can explicitly override each virtual function.
For information on explicit overrides in managed code using C++/CLI, see Explicit
Overrides.
Example
The following code example illustrates how to use explicit overrides:
C++
// deriv_ExplicitOverrides.cpp
// compile with: /GR
extern "C" int printf_s(const char *, ...);
__interface IMyInt1 {
void mf1();
void mf1(int);
void mf2();
void mf2(int);
};
__interface IMyInt2 {
void mf1();
void mf1(int);
void mf2();
void mf2(int);
};
void IMyInt1::mf1(int) {
printf_s("In CMyClass::IMyInt1::mf1(int)\n");
}
void IMyInt1::mf2();
void IMyInt1::mf2(int);
void IMyInt2::mf1() {
printf_s("In CMyClass::IMyInt2::mf1()\n");
}
void IMyInt2::mf1(int) {
printf_s("In CMyClass::IMyInt2::mf1(int)\n");
}
void IMyInt2::mf2();
void IMyInt2::mf2(int);
};
void CMyClass::IMyInt1::mf2() {
printf_s("In CMyClass::IMyInt1::mf2()\n");
}
void CMyClass::IMyInt1::mf2(int) {
printf_s("In CMyClass::IMyInt1::mf2(int)\n");
}
void CMyClass::IMyInt2::mf2() {
printf_s("In CMyClass::IMyInt2::mf2()\n");
}
void CMyClass::IMyInt2::mf2(int) {
printf_s("In CMyClass::IMyInt2::mf2(int)\n");
}
int main() {
IMyInt1 *pIMyInt1 = new CMyClass();
IMyInt2 *pIMyInt2 = dynamic_cast<IMyInt2 *>(pIMyInt1);
pIMyInt1->mf1();
pIMyInt1->mf1(1);
pIMyInt1->mf2();
pIMyInt1->mf2(2);
pIMyInt2->mf1();
pIMyInt2->mf1(3);
pIMyInt2->mf2();
pIMyInt2->mf2(4);
Output
In CMyClass::IMyInt1::mf1()
In CMyClass::IMyInt1::mf1(int)
In CMyClass::IMyInt1::mf2()
In CMyClass::IMyInt1::mf2(int)
In CMyClass::IMyInt2::mf1()
In CMyClass::IMyInt2::mf1(int)
In CMyClass::IMyInt2::mf2()
In CMyClass::IMyInt2::mf2(int)
See also
Inheritance
Abstract classes (C++)
Article • 08/03/2021
Abstract classes act as expressions of general concepts from which more specific classes
can be derived. You can't create an object of an abstract class type. However, you can
use pointers and references to abstract class types.
You create an abstract class by declaring at least one pure virtual member function.
That's a virtual function declared by using the pure specifier ( = 0 ) syntax. Classes
derived from the abstract class must implement the pure virtual function or they, too,
are abstract classes.
Consider the example presented in Virtual functions. The intent of class Account is to
provide general functionality, but objects of type Account are too general to be useful.
That means Account is a good candidate for an abstract class:
C++
// deriv_AbstractClasses.cpp
// compile with: /LD
class Account {
public:
Account( double d ); // Constructor.
virtual double GetBalance(); // Obtain balance.
virtual void PrintBalance() = 0; // Pure virtual function.
private:
double _balance;
};
The only difference between this declaration and the previous one is that PrintBalance
is declared with the pure specifier ( = 0 ).
Argument types
abstract-class-name::function-name()
Defined pure virtual functions are helpful when you design class hierarchies whose base
classes include pure virtual destructors. That's because base class destructors are always
called during object destruction. Consider the following example:
C++
// deriv_RestrictionsOnUsingAbstractClasses.cpp
// Declare an abstract base class with a pure virtual destructor.
// It's the simplest possible abstract class.
class base
{
public:
base() {}
// To define the virtual destructor outside the class:
virtual ~base() = 0;
// Microsoft-specific extension to define it inline:
// virtual ~base() = 0 {};
};
int main()
{
derived aDerived; // destructor called when it goes out of scope
}
The example shows how a Microsoft compiler extension lets you add an inline definition
to pure virtual ~base() . You can also define it outside the class by using base::~base()
{} .
When the object aDerived goes out of scope, the destructor for class derived is called.
The compiler generates code to implicitly call the destructor for class base after the
derived destructor. The empty implementation for the pure virtual function ~base
ensures that at least some implementation exists for the function. Without it, the linker
generates an unresolved external symbol error for the implicit call.
7 Note
In the preceding example, the pure virtual function base::~base is called implicitly
from derived::~derived . It's also possible to call pure virtual functions explicitly by
using a fully qualified member-function name. Such functions must have an
implementation, or the call results in an error at link time.
See also
Inheritance
Summary of Scope Rules
Article • 08/03/2021
The use of a name must be unambiguous within its scope (up to the point where
overloading is determined). If the name denotes a function, the function must be
unambiguous with respect to number and type of parameters. If the name remains
unambiguous, member-access rules are applied.
Constructor initializers
Constructor initializers are evaluated in the scope of the outermost block of the
constructor for which they are specified. Therefore, they can use the constructor's
parameter names.
Global names
A name of an object, function, or enumerator is global if it is introduced outside any
function or class or prefixed by the global unary scope operator ( :: ), and if it is not
used in conjunction with any of these binary operators:
Scope-resolution ( :: )
Qualified names
Names used with the binary scope-resolution operator ( :: ) are called "qualified names."
The name specified after the binary scope-resolution operator must be a member of the
class specified on the left of the operator or a member of its base class(es).
Names specified after the member-selection operator (. or ->) must be members of the
class type of the object specified on the left of the operator or members of its base
class(es). Names specified on the right of the member-selection operator (->) can also
be objects of another class type, provided that the left-hand side of -> is a class object
and that the class defines an overloaded member-selection operator (->) that evaluates
to a pointer to some other class type. (This provision is discussed in more detail in Class
Member Access.)
The compiler searches for names in the following order, stopping when the name is
found:
1. Current block scope if name is used inside a function; otherwise, global scope.
2. Outward through each enclosing block scope, including the outermost function
scope (which includes function parameters).
3. If the name is used inside a member function, the class's scope is searched for the
name.
5. The enclosing nested class scope (if any) and its bases are searched. The search
continues until the outermost enclosing class scope is searched.
2. Names preceded by the class , struct , and union keywords force the compiler to
search only for class , struct , or union names.
3. Names on the left side of the scope-resolution operator ( :: ) can be only class ,
struct , namespace , or union names.
If the name refers to a nonstatic member but is used in a static member function, an
error message is generated. Similarly, if the name refers to any nonstatic member in an
enclosing class, an error message is generated because enclosed classes do not have
enclosing-class this pointers.
Function parameter names in function declarations (prototypes) are in local scope of the
declaration and go out of scope at the end of the declaration.
Default parameters are in the scope of the parameter for which they are the default, as
described in the preceding two paragraphs. However, they cannot access local variables
or nonstatic class members. Default parameters are evaluated at the point of the
function call, but they are evaluated in the function declaration's original scope.
Therefore, the default parameters for member functions are always evaluated in class
scope.
See also
Inheritance
Inheritance keywords
Article • 08/03/2021
Microsoft Specific
class class-name
where:
class-name
C++ allows you to declare a pointer to a class member before the definition of the class.
For example:
C++
class S;
int S::*p;
encounters such a pointer, it must make a generalized representation of the pointer. The
size of the representation is dependent on the inheritance model specified. There are
three ways to specify an inheritance model to the compiler:
7 Note
If you always declare a pointer to a member of a class after defining the class,
you don't need to use any of these options.
If you declare a pointer to a class member before the class is defined, it can negatively
affect the size and speed of the resulting executable file. The more complex the
inheritance used by a class, the greater the number of bytes required to represent a
pointer to a member of the class. And, the larger the code required to interpret the
pointer. Single (or no) inheritance is least complex, and virtual inheritance is most
complex. Pointers to members you declare before the class is defined always use the
largest, most complex representation.
C++
class __single_inheritance S;
int S::*p;
then no matter the command-line options or pragmas you specify, pointers to members
of class S will use the smallest possible representation.
7 Note
See also
Keywords
virtual (C++)
Article • 08/03/2021
Syntax
Parameters
type-specifiers
Specifies the return type of the virtual member function.
member-function-declarator
Declares a member function.
access-specifier
Defines the level of access to the base class, public , protected or private . Can appear
before or after the virtual keyword.
base-class-name
Identifies a previously declared class type.
Remarks
See Virtual Functions for more information.
Also see the following keywords: class, private, public, and protected.
See also
Keywords
__super
Article • 08/03/2021
Microsoft Specific
Allows you to explicitly state that you are calling a base-class implementation for a
function that you are overriding.
Syntax
__super::member_function();
Remarks
All accessible base-class methods are considered during the overload resolution phase,
and the function that provides the best match is the one that is called.
__super cannot be used with a using declaration. See using Declaration for more
information.
With the introduction of attributes that inject code, your code might contain one or
more base classes whose names you may not know but that contain methods that you
wish to call.
Example
C++
// deriv_super.cpp
// compile with: /c
struct B1 {
void mf(int) {}
};
struct B2 {
void mf(short) {}
void mf(char) {}
};
struct D : B1, B2 {
void mf(short) {
__super::mf(1); // Calls B1::mf(int)
__super::mf('s'); // Calls B2::mf(char)
}
};
See also
Keywords
__interface
Article • 08/03/2021
Microsoft Specific
Syntax
Remarks
A C++ class or struct could be implemented with these rules, but __interface enforces
them.
C++
__interface IMyInterface {
HRESULT CommitX();
HRESULT get_X(BSTR* pbstrName);
};
Notice that you do not have to explicitly indicate that the CommitX and get_X functions
are pure virtual. An equivalent declaration for the first function would be:
C++
Example
The following sample shows how to use properties declared in an interface.
C++
// deriv_interface.cpp
#define _ATL_ATTRIBUTES 1
#include <atlbase.h>
#include <atlcom.h>
#include <string.h>
#include <comdef.h>
#include <stdio.h>
[module(name="test")];
[ coclass, uuid("00000000-0000-0000-0000-000000000002") ]
class MyClass : public IFace {
private:
int m_i;
BSTR m_bstr;
public:
MyClass()
{
m_i = 0;
m_bstr = 0;
}
~MyClass()
{
if (m_bstr)
::SysFreeString(m_bstr);
}
int get_int_data()
{
return m_i;
}
void put_int_data(int _i)
{
m_i = _i;
}
BSTR get_bstr_data()
{
BSTR bstr = ::SysAllocString(m_bstr);
return bstr;
}
int main()
{
_bstr_t bstr("Testing");
CoInitialize(NULL);
CComObject<MyClass>* p;
CComObject<MyClass>::CreateInstance(&p);
p->int_data = 100;
printf_s("p->int_data = %d\n", p->int_data);
p->bstr_data = bstr;
printf_s("bstr_data = %S\n", p->bstr_data);
}
Output
p->int_data = 100
bstr_data = Testing
See also
Keywords
Interface Attributes
Special member functions
Article • 08/03/2021
The special member functions are class (or struct) member functions that, in certain
cases, the compiler automatically generates for you. These functions are the default
constructor, the destructor, the copy constructor and copy assignment operator, and the
move constructor and move assignment operator. If your class does not define one or
more of the special member functions, then the compiler may implicitly declare and
define the functions that are used. The compiler-generated implementations are called
the default special member functions. The compiler does not generate functions if they
are not needed.
You can explicitly declare a default special member function by using the = default
keyword. This causes the compiler to define the function only if needed, in the same way
as if the function was not declared at all.
In some cases, the compiler may generate deleted special member functions, which are
not defined and therefore not callable. This can happen in cases where a call to a
particular special member function on a class doesn't make sense, given other
properties of the class. To explicitly prevent automatic generation of a special member
function, you can declare it as deleted by using the = delete keyword.
The default destructor performs member-wise destruction of the object. It is virtual only
if a base class destructor is virtual.
The default copy and move construction and assignment operations perform member-
wise bit-pattern copies or moves of non-static data members. Move operations are only
generated when no destructor or move or copy operations are declared. A default copy
constructor is only generated when no copy constructor is declared. It is implicitly
deleted if a move operation is declared. A default copy assignment operator is
generated only when no copy assignment operator is explicitly declared. It is implicitly
deleted if a move operation is declared.
See also
C++ Language Reference
Static Members (C++)
Article • 08/03/2021
Classes can contain static member data and member functions. When a data member is
declared as static , only one copy of the data is maintained for all objects of the class.
Static data members are not part of objects of a given class type. As a result, the
declaration of a static data member is not considered a definition. The data member is
declared in class scope, but definition is performed at file scope. These static members
have external linkage. The following example illustrates this:
C++
// static_data_members.cpp
class BufferedOutput
{
public:
// Return number of bytes written by any object of this class.
short BytesWritten()
{
return bytecount;
}
int main()
{
}
In the preceding code, the member bytecount is declared in class BufferedOutput , but it
must be defined outside the class declaration.
Static data members can be referred to without referring to an object of class type. The
number of bytes written using BufferedOutput objects can be obtained as follows:
C++
long nBytes = BufferedOutput::bytecount;
For the static member to exist, it is not necessary that any objects of the class type exist.
Static members can also be accessed using the member-selection (. and ->) operators.
For example:
C++
BufferedOutput Console;
In the preceding case, the reference to the object ( Console ) is not evaluated; the value
returned is that of the static object bytecount .
Static data members are subject to class-member access rules, so private access to static
data members is allowed only for class-member functions and friends. These rules are
described in Member-Access Control. The exception is that static data members must be
defined in file scope regardless of their access restrictions. If the data member is to be
explicitly initialized, an initializer must be provided with the definition.
The type of a static member is not qualified by its class name. Therefore, the type of
BufferedOutput::bytecount is long .
See also
Classes and Structs
User-Defined Type Conversions (C++)
Article • 08/03/2021
A conversion produces a new value of some type from a value of a different type.
Standard conversions are built into the C++ language and support its built-in types, and
you can create user-defined conversions to perform conversions to, from, or between
user-defined types.
The standard conversions perform conversions between built-in types, between pointers
or references to types related by inheritance, to and from void pointers, and to the null
pointer. For more information, see Standard Conversions. User-defined conversions
perform conversions between user-defined types, or between user-defined types and
built-in types. You can implement them as Conversion constructors or as Conversion
functions.
Conversions can either be explicit—when the programmer calls for one type to be
converted to another, as in a cast or direct initialization—or implicit—when the
language or program calls for a different type than the one given by the programmer.
An argument supplied to a function does not have the same type as the matching
parameter.
The value returned from a function does not have the same type as the function
return type.
An initializer expression does not have the same type as the object it is initializing.
An operand supplied to an operator does not have the same type as the matching
operand-parameter. For built-in operators, both operands must have the same
type, and are converted to a common type that can represent both. For more
information, see Standard Conversions. For user-defined operators, each operand
must have the same type as the matching operand-parameter.
When one standard conversion can't complete an implicit conversion, the compiler can
use a user-defined conversion, followed optionally by an additional standard conversion,
to complete it.
When two or more user-defined conversions that perform the same conversion are
available at a conversion site, the conversion is said to be ambiguous. Such ambiguities
are an error because the compiler can't determine which one of the available
conversions it should choose. However, it's not an error just to define multiple ways of
performing the same conversion because the set of available conversions can be
different at different locations in the source code—for example, depending on which
header files are included in a source file. As long as only one conversion is available at
the conversion site, there is no ambiguity. There are several ways that ambiguous
conversions can arise, but the most common ones are:
Multiple inheritance. The conversion is defined in more than one base class.
You can usually resolve an ambiguity just by qualifying the name of the involved type
more fully or by performing an explicit cast to clarify your intent.
One well-known example of an implicit conversion that can cause problems is the
conversion to bool . There are many reasons that you might want to create a class type
that can be used in a Boolean context—for example, so that it can be used to control an
if statement or loop—but when the compiler performs a user-defined conversion to a
built-in type, the compiler is allowed to apply an additional standard conversion
afterwards. The intent of this additional standard conversion is to allow for things like
promotion from short to int , but it also opens the door for less-obvious conversions—
for example, from bool to int , which allows your class type to be used in integer
contexts you never intended. This particular problem is known as the Safe Bool Problem.
This kind of problem is where the explicit keyword can help.
The explicit keyword tells the compiler that the specified conversion can't be used to
perform implicit conversions. If you wanted the syntactic convenience of implicit
conversions before the explicit keyword was introduced, you had to either accept the
unintended consequences that implicit conversion sometimes created or use less-
convenient, named conversion functions as a workaround. Now, by using the explicit
keyword, you can create convenient conversions that can only be used to perform
explicit casts or direct initialization, and that won't lead to the kind of problems
exemplified by the Safe Bool Problem.
The explicit keyword can be applied to conversion constructors since C++98, and to
conversion functions since C++11. The following sections contain more information
about how to use the explicit keyword.
Conversion constructors
Conversion constructors define conversions from user-defined or built-in types to a
user-defined type. The following example demonstrates a conversion constructor that
converts from the built-in type double to a user-defined type Money .
C++
#include <iostream>
class Money
{
public:
Money() : amount{ 0.0 } {};
Money(double _amount) : amount{ _amount } {};
double amount;
};
display_balance(payable);
display_balance(49.95);
display_balance(9.99f);
return 0;
}
Notice that the first call to the function display_balance , which takes an argument of
type Money , doesn't require a conversion because its argument is the correct type.
However, on the second call to display_balance , a conversion is needed because the
type of the argument, a double with a value of 49.95 , is not what the function expects.
The function can't use this value directly, but because there's a conversion from the type
of the argument— double —to the type of the matching parameter— Money —a
temporary value of type Money is constructed from the argument and used to complete
the function call. In the third call to display_balance , notice that the argument is not a
double , but is instead a float with a value of 9.99 —and yet the function call can still
be completed because the compiler can perform a standard conversion—in this case,
from float to double —and then perform the user-defined conversion from double to
Money to complete the necessary conversion.
The target type of the conversion is the user-defined type that's being constructed.
Conversion constructors typically take exactly one argument, which is of the source
type. However, a conversion constructor can specify additional parameters if each
additional parameter has a default value. The source type remains the type of the
first parameter.
C++
#include <iostream>
class Money
{
public:
Money() : amount{ 0.0 } {};
explicit Money(double _amount) : amount{ _amount } {};
double amount;
};
return 0;
}
In this example, notice that you can still use the explicit conversion constructor to
perform direct initialization of payable . If instead you were to copy-initialize Money
payable = 79.99; , it would be an error. The first call to display_balance is unaffected
because the argument is the correct type. The second call to display_balance is an
error, because the conversion constructor can't be used to perform implicit conversions.
The third call to display_balance is legal because of the explicit cast to Money , but
notice that the compiler still helped complete the cast by inserting an implicit cast from
float to double .
C++
#include <iostream>
class Money
{
public:
Money() : amount{ 0.0 } {};
Money(double _amount) : amount{ _amount } {};
Notice that the member variable amount is made private and that a public conversion
function to type double is introduced just to return the value of amount . In the function
display_balance , an implicit conversion occurs when the value of balance is streamed
to standard output by using the stream insertion operator << . Because no stream-
insertion operator is defined for the user-defined type Money , but there is one for built-
in type double , the compiler can use the conversion function from Money to double to
satisfy the stream-insertion operator.
The target type of the conversion must be declared prior to the declaration of the
conversion function. Classes, structures, enumerations, and typedefs cannot be
declared within the declaration of the conversion function.
C++
Conversion functions have a return type that is specified by the name of the
conversion function, which is also the name of the conversion's target type.
Specifying a return type in the declaration is an error.
C++
#include <iostream>
class Money
{
public:
Money() : amount{ 0.0 } {};
Money(double _amount) : amount{ _amount } {};
Here the conversion function operator double has been made explicit, and an explicit
cast to type double has been introduced in the function display_balance to perform the
conversion. If this cast were omitted, the compiler would be unable to locate a suitable
stream-insertion operator << for type Money and an error would occur.
Mutable Data Members (C++)
Article • 03/15/2024
This keyword can only be applied to non-static, non-const, and non-reference data
members of a class. If a data member is declared mutable , then it is legal to assign a
value to this data member from a const member function.
Syntax
mutable member-variable-declaration;
Remarks
For example, the following code will compile without error because m_accessCount has
been declared to be mutable , and therefore can be modified by GetFlag even though
GetFlag is a const member function.
C++
// mutable.cpp
class X
{
public:
bool GetFlag() const
{
m_accessCount++;
return m_flag;
}
private:
bool m_flag;
mutable int m_accessCount;
};
See also
Keywords
Feedback
Was this page helpful? Yes No
A class can be declared within the scope of another class. Such a class is called a "nested
class." Nested classes are considered to be within the scope of the enclosing class and
are available for use within that scope. To refer to a nested class from a scope other than
its immediate enclosing scope, you must use a fully qualified name.
C++
// nested_class_declarations.cpp
class BufferedIO
{
public:
enum IOError { None, Access, General };
int main()
{
}
BufferedIO . These class names are not visible outside the scope of class BufferedIO .
However, an object of type BufferedIO does not contain any objects of types
BufferedInput or BufferedOutput .
Nested classes can directly use names, type names, names of static members, and
enumerators only from the enclosing class. To use names of other class members, you
must use pointers, references, or object names.
7 Note
Nested classes declare only types within class scope. They do not cause contained
objects of the nested class to be created. The preceding example declares two
nested classes but does not declare any objects of these class types.
An exception to the scope visibility of a nested class declaration is when a type name is
declared together with a forward declaration. In this case, the class name declared by
the forward declaration is visible outside the enclosing class, with its scope defined to
be the smallest enclosing non-class scope. For example:
C++
// nested_class_declarations_2.cpp
class C
{
public:
typedef class U u_t; // class U visible outside class C scope
typedef class V {} v_t; // class V not visible outside class C
};
int main()
{
// okay, forward declaration used above so file scope is used
U* pu;
C++
// member_functions_in_nested_classes.cpp
class BufferedIO
{
public:
enum IOError { None, Access, General };
class BufferedInput
{
public:
int read(); // Declare but do not define member
int good(); // functions read and good.
private:
IOError _inputerror;
};
class BufferedOutput
{
// Member list.
};
};
// Define member functions read and good in
// file scope.
int BufferedIO::BufferedInput::read()
{
return(1);
}
int BufferedIO::BufferedInput::good()
{
return _inputerror == None;
}
int main()
{
}
In the preceding example, the qualified-type-name syntax is used to declare the function
name. The declaration:
C++
BufferedIO::BufferedInput::read()
means "the read function that is a member of the BufferedInput class that is in the
scope of the BufferedIO class." Because this declaration uses the qualified-type-name
syntax, constructs of the following form are possible:
C++
int BIO_INPUT::read()
The preceding declaration is equivalent to the previous one, but it uses a typedef name
in place of the class names.
C++
// friend_functions_and_nested_classes.cpp
#include <string.h>
enum
{
sizeOfMessage = 255
};
char *rgszMessage[sizeOfMessage];
class BufferedIO
{
public:
class BufferedInput
{
public:
friend int GetExtendedErrorStatus();
static char *message;
static int messageSize;
int iMsgNo;
};
};
char *BufferedIO::BufferedInput::message;
int BufferedIO::BufferedInput::messageSize;
int GetExtendedErrorStatus()
{
int iMsgNo = 1; // assign arbitrary value as message number
strcpy_s( BufferedIO::BufferedInput::message,
BufferedIO::BufferedInput::messageSize,
rgszMessage[iMsgNo] );
return iMsgNo;
}
int main()
{
}
C++
With the preceding interface, several classes can use the services of this function by
passing a memory location where they want the error message copied.
See also
Classes and Structs
Anonymous Class Types
Article • 08/03/2021
Classes can be anonymous — that is, they can be declared without an identifier. This is
useful when you replace a class name with a typedef name, as in the following:
C++
typedef struct
{
unsigned x;
unsigned y;
} POINT;
7 Note
The use of anonymous classes shown in the previous example is useful for
preserving compatibility with existing C code. In some C code, the use of typedef
in conjunction with anonymous structures is prevalent.
Anonymous classes are also useful when you want a reference to a class member to
appear as though it were not contained in a separate class, as in the following:
C++
struct PTValue
{
POINT ptLoc;
union
{
int iValue;
long lValue;
};
};
PTValue ptv;
In the preceding code, iValue can be accessed using the object member-selection
operator (.) as follows:
C++
int i = ptv.iValue;
Anonymous classes are subject to certain restrictions. (For more information about
anonymous unions, see Unions.) Anonymous classes:
Anonymous structs
Microsoft Specific
You can access the members of an anonymous structure as if they were members in the
containing structure.
C++
// anonymous_structures.c
#include <stdio.h>
struct phone
{
int areacode;
long number;
};
struct person
{
char name[30];
char gender;
int age;
int weight;
struct phone; // Anonymous structure; no name needed
} Jim;
int main()
{
Jim.number = 1234567;
printf_s("%d\n", Jim.number);
}
//Output: 1234567
END Microsoft Specific
Pointers to Members
Article • 08/03/2021
The type specifier: the name of a type. It's the type of the member to be
pointed to, not the class.
2. The declarator:
The qualified name of the class containing the members to be pointed to.
The :: operator.
The * operator.
The = operator.
The :: operator.
A pointer to a member of a class differs from a normal pointer: it has both type
information for the type of the member and for the class to which the member belongs.
A normal pointer identifies (has the address of) only a single object in memory. A
pointer to a member of a class identifies that member in any instance of the class. The
following example declares a class, Window , and some pointers to member data.
C++
// pointers_to_members1.cpp
class Window
{
public:
Window(); // Default constructor.
Window( int x1, int y1, // Constructor specifying
int x2, int y2 ); // Window size.
bool SetCaption( const char *szTitle ); // Set window caption.
const char *GetCaption(); // Get window caption.
char *szWinCaption; // Window caption.
};
In the preceding example, pwCaption is a pointer to any member of class Window that's
of type char* . The type of pwCaption is char * Window::* . The next code fragment
declares pointers to the SetCaption and GetCaption member functions.
C++
The pointers pfnwGC and pfnwSC point to GetCaption and SetCaption of the Window
class, respectively. The code copies information to the window caption directly using the
pointer to member pwCaption :
C++
Window wMainWindow;
Window *pwChildWindow = new Window;
char *szUntitled = "Untitled - ";
int cUntitledLen = strlen( szUntitled );
The difference between the .* and ->* operators (the pointer-to-member operators) is
that the .* operator selects members given an object or object reference, while the ->*
operator selects members through a pointer. For more information about these
operators, see Expressions with Pointer-to-Member Operators.
The result of the pointer-to-member operators is the type of the member. In this case,
it's char * .
The following code fragment invokes the member functions GetCaption and SetCaption
using pointers to members:
C++
// Allocate a buffer.
enum {
sizeOfBuffer = 100
};
char szCaptionBase[sizeOfBuffer];
The key to virtual functions working, as always, is invoking them through a pointer to a
base class. (For more information about virtual functions, see Virtual Functions.)
The following code shows how to invoke a virtual function through a pointer-to-
member function:
C++
// virtual_functions.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
class Base
{
public:
virtual void Print();
};
void (Base::* bfnPrint)() = &Base::Print;
void Base::Print()
{
cout << "Print function for class Base" << endl;
}
void Derived::Print()
{
cout << "Print function for class Derived" << endl;
}
int main()
{
Base *bPtr;
Base bObject;
Derived dObject;
bPtr = &bObject; // Set pointer to address of bObject.
(bPtr->*bfnPrint)();
bPtr = &dObject; // Set pointer to address of dObject.
(bPtr->*bfnPrint)();
}
// Output:
// Print function for class Base
// Print function for class Derived
The this pointer
Article • 12/14/2023
The this pointer is a pointer accessible only within the nonstatic member functions of a
class , struct , or union type. It points to the object for which the member function is
Syntax
C++
this
this->member-identifier
Remarks
An object's this pointer isn't part of the object itself. It's not part of the result of a
sizeof statement on the object. When a nonstatic member function is called for an
object, the compiler passes the object's address to the function as a hidden argument.
For example, the following function call:
C++
myDate.setMonth( 3 );
C++
setMonth( &myDate, 3 );
The object's address is available from within the member function as the this pointer.
Most this pointer uses are implicit. It's legal, though unnecessary, to use an explicit
this when referring to members of the class. For example:
C++
The expression *this is commonly used to return the current object from a member
function:
C++
return *this;
C++
if (&Object != this) {
// do not execute in cases of self-reference
7 Note
Because the this pointer is nonmodifiable, assignments to the this pointer are
not allowed. Earlier implementations of C++ allowed assignment to this .
Example
C++
// this_pointer.cpp
// compile with: /EHsc
#include <iostream>
#include <string.h>
class Buf
{
public:
Buf( char* szBuffer, size_t sizeOfBuffer );
Buf& operator=( const Buf & );
void Display() { cout << buffer << endl; }
private:
char* buffer;
size_t sizeOfBuffer;
};
int main()
{
Buf myBuf( "my buffer", 10 );
Buf yourBuf( "your buffer", 12 );
// assignment operator
myBuf = yourBuf;
Output
my buffer
your buffer
Type of the this pointer
The this pointer's type changes depending on whether the function declaration
includes the const and/or volatile keywords. The following syntax describes the type
of this in a member function:
The this pointer can't be reassigned. The const or volatile qualifiers used in the
member function declaration apply to the class instance the this pointer points at, in
the scope of that function, as shown in the following table:
ノ Expand table
Member function declaration type of this pointer for a class named myClass
ノ Expand table
Modifier Meaning
const Can't change member data; can't invoke member functions that aren't const .
volatile Member data is loaded from memory each time it's accessed; disables certain
optimizations.
It's an error to pass a const object to a member function that isn't const .
Similarly, it's also an error to pass a volatile object to a member function that isn't
volatile .
Member functions declared as const can't change member data. In const functions, the
this pointer is a pointer to a const object.
7 Note
See also
Keywords
C++ Bit Fields
Article • 04/04/2023
Classes and structures can contain members that occupy less storage than an integral
type. These members are specified as bit fields. The syntax for bit-field member-
declarator specification follows:
Syntax
declarator : constant-expression
Remarks
The (optional) declarator is the name by which the member is accessed in the program.
It must be an integral type (including enumerated types). The constant-expression
specifies the number of bits the member occupies in the structure. Anonymous bit fields
—that is, bit-field members with no identifier—can be used for padding.
7 Note
An unnamed bit field of width 0 forces alignment of the next bit field to the next
type boundary, where type is the type of the member.
C++
// bit_fields1.cpp
// compile with: /LD
struct Date {
unsigned short nWeekDay : 3; // 0..7 (3 bits)
unsigned short nMonthDay : 6; // 0..31 (6 bits)
unsigned short nMonth : 5; // 0..12 (5 bits)
unsigned short nYear : 8; // 0..100 (8 bits)
};
The conceptual memory layout of an object of type Date is shown in the following
figure:
nYear is 8 bits long, which would overflow the word boundary of the declared type,
unsigned short . Therefore, it starts at the beginning of a new unsigned short . It isn't
necessary that all bit fields fit in one object of the underlying type; new units of storage
are allocated, according to the number of bits requested in the declaration.
Microsoft Specific
The ordering of data declared as bit fields is from low to high bit, as shown in the
previous figure.
C++
// bit_fields2.cpp
// compile with: /LD
struct Date {
unsigned nWeekDay : 3; // 0..7 (3 bits)
unsigned nMonthDay : 6; // 0..31 (6 bits)
unsigned : 0; // Force alignment to next boundary.
unsigned nMonth : 5; // 0..12 (5 bits)
unsigned nYear : 8; // 0..100 (8 bits)
};
The underlying type of a bit field must be an integral type, as described in Built-in types.
If the initializer for a reference of type const T& is an lvalue that refers to a bit field of
type T , the reference isn't bound to the bit field directly. Instead, the reference is bound
to a temporary initialized to hold the value of the bit field.
Restrictions on bit fields
The following list details erroneous operations on bit fields:
See also
Classes and Structs