0% found this document useful (0 votes)
11 views71 pages

C++ 14 Inheritance

Uploaded by

btech10253.19
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views71 pages

C++ 14 Inheritance

Uploaded by

btech10253.19
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 71

Inheritance (C++)

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++

class Derived : [virtual] [access-specifier] Base


{
// member list
};
class Derived : [virtual] [access-specifier] Base1,
[virtual] [access-specifier] Base2, . . .
{
// member list
};

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.

The following topics are included:

Single inheritance
Multiple base classes

Virtual functions

Explicit overrides

Abstract classes

Summary of scope rules

The __super and __interface keywords are documented in this section.

See also
C++ Language Reference
Virtual Functions
Article • 04/07/2022

A virtual function is a member function that you expect to be redefined in derived


classes. When you refer to a derived class object using a pointer or a reference to the
base class, you can call a virtual function for that object and execute the derived class's
version of the function.

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;
};

class CheckingAccount : public Account {


public:
CheckingAccount(double d) : Account(d) {}
void PrintBalance() { cout << "Checking account balance: " <<
GetBalance() << endl; }
};

class SavingsAccount : public Account {


public:
SavingsAccount(double d) : Account(d) {}
void PrintBalance() { cout << "Savings account balance: " <<
GetBalance(); }
};
int main() {
// Create objects of type CheckingAccount and SavingsAccount.
CheckingAccount checking( 100.00 );
SavingsAccount savings( 1000.00 );

// Call PrintBalance using a pointer to Account.


Account *pAccount = &checking;
pAccount->PrintBalance();

// Call PrintBalance using a pointer to Account.


pAccount = &savings;
pAccount->PrintBalance();
}

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 .

If a class is declared that does not provide an overriding implementation of the


PrintBalance function, the default implementation from the base class Account is used.

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.

A call to a nonvirtual function is resolved according to the type of the pointer or


reference.

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.
};

// Implement the two functions.


void Base::NameOf() {
cout << "Base::NameOf\n";
}

void Base::InvokingClass() {
cout << "Invoked by Base\n";
}

class Derived : public Base {


public:
void NameOf(); // Virtual function.
void InvokingClass(); // Nonvirtual function.
};

// Implement the two functions.


void Derived::NameOf() {
cout << "Derived::NameOf\n";
}

void Derived::InvokingClass() {
cout << "Invoked by Derived\n";
}

int main() {
// Declare an object of type Derived.
Derived aDerived;

// Declare two pointers, one of type Derived * and the other


// of type Base *, and initialize them to point to aDerived.
Derived *pDerived = &aDerived;
Base *pBase = &aDerived;

// Call the functions.


pBase->NameOf(); // Call virtual function.
pBase->InvokingClass(); // Call nonvirtual function.
pDerived->NameOf(); // Call virtual function.
pDerived->InvokingClass(); // Call nonvirtual function.
}

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

object of type Derived .

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.)

The virtual function-call mechanism can be suppressed by explicitly qualifying the


function name using the scope-resolution operator ( :: ). Consider the earlier example
involving the Account class. To call PrintBalance in the base class, use code such as the
following:

C++

CheckingAccount *pChecking = new CheckingAccount( 100.00 );

pChecking->Account::PrintBalance(); // Explicit qualification.

Account *pAccount = pChecking; // Call Account::PrintBalance

pAccount->Account::PrintBalance(); // Explicit qualification.

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.

Simple Single-Inheritance Graph

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

declaration of such a class hierarchy is shown in the following example:

C++

// deriv_SingleInheritance.cpp
// compile with: /LD
class PrintedDocument {};

// Book is derived from PrintedDocument.


class Book : public PrintedDocument {};

// PaperbackBook is derived from Book.


class PaperbackBook : public Book {};

PrintedDocument is considered a "direct base" class to Book ; it is an "indirect base" class

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.

Sample of Directed Acyclic Graph

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.
};

// Implementation of PrintNameOf function from class Document.


void Document::PrintNameOf() {
cout << Name << endl;
}

class Book : public Document {


public:
Book( char *name, long pagecount );
private:
long PageCount;
};

// Constructor from class Book.


Book::Book( char *name, long pagecount ) {
Name = new char[ strlen( name ) + 1 ];
strcpy_s( Name, strlen(Name), name );
PageCount = pagecount;
};

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++

// Create a new object of type Book. This invokes the


// constructor Book::Book.
Book LibraryBook( "Programming Windows, 2nd Ed", 944 );

...

// Use PrintNameOf function inherited from class Document.


LibraryBook.PrintNameOf();

As the preceding example demonstrates, class-member and inherited data and


functions are used identically. If the implementation for class Book calls for a
reimplementation of the PrintNameOf function, the function that belongs to the
Document class can be called only by using the scope-resolution ( :: ) operator:

C++

// deriv_SingleInheritance3.cpp
// compile with: /EHsc /LD
#include <iostream>
using namespace std;

class Document {
public:
char *Name; // Document name.
void PrintNameOf() {} // Print name.
};

class Book : public Document {


Book( char *name, long pagecount );
void PrintNameOf();
long PageCount;
};
void Book::PrintNameOf() {
cout << "Name of book: ";
Document::PrintNameOf();
}

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() {}
};

class PaperbackBook : public Document {};

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.

Multiple instances of a single base class

In the figure, pictorial representations of the components of CollectibleString and


CollectibleSortable are shown. However, the base class, Collectible , is in
CollectibleSortableString through the CollectibleString path and the

CollectibleSortable path. To eliminate this redundancy, such classes can be declared as

virtual base classes when they are inherited.


Multiple Base Classes
Article • 04/10/2023

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:

Simulated lunch-line graph

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):

Simulated lunch-line object


There are two Queue subobjects in the LunchCashierQueue object. The following code
declares Queue to be a virtual base class:

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).

Simulated lunch-line object with virtual base classes

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:

Virtual and nonvirtual components of the same class

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++

#pragma vtordisp( off )


class GetReal : virtual public { ... };
\#pragma vtordisp( on )

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;
};

// Define class C as derived from A and B.


class C : public A, public B {};

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.

The compiler detects ambiguities by performing tests in this order:

1. If access to the name is ambiguous (as just described), an error message is


generated.

2. If overloaded functions are unambiguous, they're resolved.

3. If access to the name violates member-access permission, an error message is


generated. (For more information, see Member-Access Control.)

When an expression produces an ambiguity through inheritance, you can manually


resolve it by qualifying the name in question with its class name. To make the preceding
example compile properly with no ambiguities, use code such as:
C++

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;
};

class B : public virtual A {


public:
int a();
};

class C : public virtual A {};

class D : public B, public C {


public:
D() { a(); } // Not ambiguous. B::a() dominates A::a.
};
Ambiguous conversions
Explicit and implicit conversions from pointers or references to class types can cause
ambiguities. The next figure, Ambiguous Conversion of Pointers to Base Classes, shows
the following:

The declaration of an object of type D .

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.

Ambiguous conversion of pointers to base classes

The conversion to type A* (pointer to A ) is ambiguous because there's no way to


discern which subobject of type A is the correct one. You can avoid the ambiguity by
explicitly specifying which subobject you mean to use, as follows:

C++

(A *)(B *)&d // Use B subobject.


(A *)(C *)&d // Use C subobject.

Ambiguities and virtual base classes


If virtual base classes are used, functions, objects, types, and enumerators can be
reached through multiple-inheritance paths. Because there's only one instance of the
base class, there's no ambiguity when accessing these names.

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.

END Microsoft Specific

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);
};

class CMyClass : public IMyInt1, public IMyInt2 {


public:
void IMyInt1::mf1() {
printf_s("In CMyClass::IMyInt1::mf1()\n");
}

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);

// Cast to a CMyClass pointer so that the destructor gets called


CMyClass *p = dynamic_cast<CMyClass *>(pIMyInt1);
delete p;
}

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 ).

Restrictions on abstract classes


Abstract classes can't be used for:

Variables or member data

Argument types

Function return types

Types of explicit conversions


If the constructor for an abstract class calls a pure virtual function, either directly or
indirectly, the result is undefined. However, constructors and destructors for abstract
classes can call other member functions.

Defined pure virtual functions


Pure virtual functions in abstract classes can be defined, or have an implementation. You
can only call such functions by using the fully qualified syntax:

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 {};
};

base::~base() {} // required if not using Microsoft extension

class derived : public base


{
public:
derived() {}
~derived() {}
};

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 ( :: )

Member-selection for objects and references (.)

Member-selection for pointers (->)

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.

4. The class's base classes are 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.

6. Global scope is searched.

However, you can make modifications to this search order as follows:

1. Names preceded by :: force the search to begin at global scope.

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


Function parameter names in function definitions are considered to be in the scope of
the outermost block of the function. Therefore, they are local names and go out of
scope when the function is exited.

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

class __single_inheritance class-name

class __multiple_inheritance class-name


class __virtual_inheritance class-name

where:

class-name

The name of the class being declared.

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;

In the code above, p is declared to be a pointer to integer member of class S. However,


class S hasn't been defined yet in this code; it's only been declared. When the compiler

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:

At the command line using the /vmg switch

Using the pointers_to_members pragma

Using the inheritance keywords __single_inheritance , __multiple_inheritance ,


and __virtual_inheritance . This technique controls the inheritance model on a
per-class basis.

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.

If the example above is changed to:

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

The same forward declaration of a class pointer-to-member representation should


occur in every translation unit that declares pointers to members of that class, and
the declaration should occur before the pointers to members are declared.

For compatibility with previous versions, _single_inheritance , _multiple_inheritance ,


and _virtual_inheritance are synonyms for __single_inheritance ,
__multiple_inheritance , and __virtual_inheritance unless compiler option /Za

(Disable language extensions) is specified.

END Microsoft Specific

See also
Keywords
virtual (C++)
Article • 08/03/2021

The virtual keyword declares a virtual function or a virtual base class.

Syntax

virtual [type-specifiers] member-function-declarator


virtual [access-specifier] base-class-name

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 can only appear within the body of a member function.

__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)
}
};

END Microsoft Specific

See also
Keywords
__interface
Article • 08/03/2021

Microsoft Specific

A Microsoft C++ interface can be defined as follows:

Can inherit from zero or more base interfaces.

Cannot inherit from a base class.

Can only contain public, pure virtual methods.

Cannot contain constructors, destructors, or operators.

Cannot contain static methods.

Cannot contain data members; properties are allowed.

Syntax

modifier __interface interface-name {interface-definition};

Remarks
A C++ class or struct could be implemented with these rules, but __interface enforces
them.

For example, the following is a sample interface definition:

C++

__interface IMyInterface {
HRESULT CommitX();
HRESULT get_X(BSTR* pbstrName);
};

For information on managed interfaces, see interface class.

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++

virtual HRESULT CommitX() = 0;

__interface implies the novtable __declspec modifier.

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")];

[ object, uuid("00000000-0000-0000-0000-000000000001"), library_block ]


__interface IFace {
[ id(0) ] int int_data;
[ id(5) ] BSTR bstr_data;
};

[ 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;
}

void put_bstr_data(BSTR bstr)


{
if (m_bstr)
::SysFreeString(m_bstr);
m_bstr = ::SysAllocString(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

END Microsoft Specific

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 compiler generates a default constructor, a constructor that takes no arguments,


only when you have not declared any other constructor. If you have declared only a
constructor that takes parameters, code that attempts to call a default constructor
causes the compiler to produce an error message. The compiler-generated default
constructor performs simple member-wise default initialization of the object. Default
initialization leaves all member variables in an indeterminate state.

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;
}

// Reset the counter.


static void ResetCount()
{
bytecount = 0;
}

// Static member declaration.


static long bytecount;
};

// Define bytecount in file scope.


long BufferedOutput::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;

long nBytes = Console.bytecount;

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.

Implicit conversions are attempted when:

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 expression that controls a conditional statement, looping construct, or switch


does not have the result type that's required to control it.

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.

Ambiguous function call. The conversion is defined as a conversion constructor of


the target type and as a conversion function of the source type. For more
information, see Conversion functions.

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.

Both conversion constructors and conversion functions obey member-access control


rules, but the accessibility of the conversions is only considered if and when an
unambiguous conversion can be determined. This means that a conversion can be
ambiguous even if the access level of a competing conversion would prevent it from
being used. For more information about member accessibility, see Member Access
Control.

The explicit keyword and problems with


implicit conversion
By default when you create a user-defined conversion, the compiler can use it to
perform implicit conversions. Sometimes this is what you want, but other times the
simple rules that guide the compiler in making implicit conversions can lead it to accept
code that you don't want it to.

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;
};

void display_balance(const Money balance)


{
std::cout << "The balance is: " << balance.amount << std::endl;
}

int main(int argc, char* argv[])


{
Money payable{ 79.99 };

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.

Declaring conversion constructors


The following rules apply to declaring a conversion constructor:

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.

Conversion constructors, like all constructors, do not specify a return type.


Specifying a return type in the declaration is an error.

Conversion constructors can be explicit.

Explicit conversion constructors


By declaring a conversion constructor to be explicit , it can only be used to perform
direct initialization of an object or to perform an explicit cast. This prevents functions
that accept an argument of the class type from also implicitly accepting arguments of
the conversion constructor's source type, and prevents the class type from being copy-
initialized from a value of the source type. The following example demonstrates how to
define an explicit conversion constructor, and the effect it has on what code is well-
formed.

C++

#include <iostream>

class Money
{
public:
Money() : amount{ 0.0 } {};
explicit Money(double _amount) : amount{ _amount } {};

double amount;
};

void display_balance(const Money balance)


{
std::cout << "The balance is: " << balance.amount << std::endl;
}

int main(int argc, char* argv[])


{
Money payable{ 79.99 }; // Legal: direct initialization is
explicit.

display_balance(payable); // Legal: no conversion required


display_balance(49.95); // Error: no suitable conversion exists
to convert from double to Money.
display_balance((Money)9.99f); // Legal: explicit cast to Money

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 .

Although the convenience of allowing implicit conversions can be tempting, doing so


can introduce hard-to-find bugs. The rule of thumb is to make all conversion
constructors explicit except when you're sure that you want a specific conversion to
occur implicitly.
Conversion functions
Conversion functions define conversions from a user-defined type to other types. These
functions are sometimes referred to as "cast operators" because they, along with
conversion constructors, are called when a value is cast to a different type. The following
example demonstrates a conversion function that converts from the user-defined type,
Money , to a built-in type, double :

C++

#include <iostream>

class Money
{
public:
Money() : amount{ 0.0 } {};
Money(double _amount) : amount{ _amount } {};

operator double() const { return amount; }


private:
double amount;
};

void display_balance(const Money balance)


{
std::cout << "The balance is: " << balance << std::endl;
}

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.

Conversion functions are inherited by derived classes. Conversion functions in a derived


class only override an inherited conversion function when they convert to exactly the
same type. For example, a user-defined conversion function of the derived class
operator int does not override—or even influence—a user-defined conversion function
of the base class operator short, even though the standard conversions define a
conversion relationship between int and short .

Declaring conversion functions


The following rules apply to declaring a conversion function:

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++

operator struct String { char string_storage; }() // illegal

Conversion functions take no arguments. Specifying any parameters in the


declaration is an error.

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.

Conversion functions can be virtual.

Conversion functions can be explicit.

Explicit conversion functions


When a conversion function is declared to be explicit, it can only be used to perform an
explicit cast. This prevents functions that accept an argument of the conversion
function's target type from also implicitly accepting arguments of the class type, and
prevents instances of the target type from being copy-initialized from a value of the
class type. The following example demonstrates how to define an explicit conversion
function and the effect it has on what code is well-formed.

C++

#include <iostream>

class Money
{
public:
Money() : amount{ 0.0 } {};
Money(double _amount) : amount{ _amount } {};

explicit operator double() const { return amount; }


private:
double amount;
};

void display_balance(const Money balance)


{
std::cout << "The balance is: " << (double)balance << std::endl;
}

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

Provide product feedback | Get help at Microsoft Q&A


Nested Class Declarations
Article • 08/03/2021

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.

The following example shows how to declare nested classes:

C++

// nested_class_declarations.cpp
class BufferedIO
{
public:
enum IOError { None, Access, General };

// Declare nested class BufferedInput.


class BufferedInput
{
public:
int read();
int good()
{
return _inputerror == None;
}
private:
IOError _inputerror;
};

// Declare nested class BufferedOutput.


class BufferedOutput
{
// Member list
};
};

int main()
{
}

BufferedIO::BufferedInput and BufferedIO::BufferedOutput are declared within

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.

In the preceding BufferedIO example, the enumeration IOError can be accessed


directly by member functions in the nested classes, BufferedIO::BufferedInput or
BufferedIO::BufferedOutput , as shown in function good .

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;

// error, type name only exists in class C scope


u_t* pu2; // C2065

// error, class defined above so class C scope


V* pv; // C2065

// okay, fully qualified name


C::V* pv2;
}

Access privilege in nested classes


Nesting a class within another class does not give special access privileges to member
functions of the nested class. Similarly, member functions of the enclosing class have no
special access to members of the nested class.

Member functions in nested classes


Member functions declared in nested classes can be defined in file scope. The preceding
example could have been written:

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++

typedef BufferedIO::BufferedInput BIO_INPUT;

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.

Friend functions in nested classes


Friend functions declared in a nested class are considered to be in the scope of the
nested class, not the enclosing class. Therefore, the friend functions gain no special
access privileges to members or member functions of the enclosing class. If you want to
use a name that is declared in a nested class in a friend function and the friend function
is defined in file scope, use qualified type names as follows:

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()
{
}

The following code shows the function GetExtendedErrorStatus declared as a friend


function. In the function, which is defined in file scope, a message is copied from a static
array into a class member. Note that a better implementation of
GetExtendedErrorStatus is to declare it as:

C++

int GetExtendedErrorStatus( char *message )

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:

Cannot have a constructor or destructor.

Cannot be passed as arguments to functions (unless type checking is defeated


using ellipsis).

Cannot be returned as return values from functions.

Anonymous structs
Microsoft Specific

A Microsoft C extension allows you to declare a structure variable within another


structure without giving it a name. These nested structures are called anonymous
structures. C++ does not allow anonymous structures.

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

Declarations of pointers to members are special cases of pointer declarations. They're


declared using the following sequence:

storage-class-specifiersopt cv-qualifiersopt type-specifier ms-modifieropt qualified-


name ::* cv-qualifiersopt identifier pm-initializeropt ;

1. The declaration specifier:

An optional storage class specifier.

Optional const and volatile specifiers.

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:

An optional Microsoft-specific modifier. For more information, see Microsoft-


Specific Modifiers.

The qualified name of the class containing the members to be pointed to.

The :: operator.

The * operator.

Optional const and volatile specifiers.

The identifier naming the pointer to member.

3. An optional pointer-to-member initializer:

The = operator.

The & operator.

The qualified name of the class.

The :: operator.

The name of a non-static member of the class of the appropriate type.


As always, multiple declarators (and any associated initializers) are allowed in a single
declaration. A pointer to member may not point to a static member of the class, a
member of reference type, or void .

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.
};

// Declare a pointer to the data member szWinCaption.


char * Window::* pwCaption = &Window::szWinCaption;
int main()
{
}

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++

const char * (Window::* pfnwGC)() = &Window::GetCaption;


bool (Window::* pfnwSC)( const char * ) = &Window::SetCaption;

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 );

strcpy_s( wMainWindow.*pwCaption, cUntitledLen, szUntitled );


(wMainWindow.*pwCaption)[cUntitledLen - 1] = '1'; // same as
// wMainWindow.SzWinCaption [cUntitledLen - 1] = '1';
strcpy_s( pwChildWindow->*pwCaption, cUntitledLen, szUntitled );
(pwChildWindow->*pwCaption)[cUntitledLen - 1] = '2'; // same as
// pwChildWindow->szWinCaption[cUntitledLen - 1] = '2';

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];

// Copy the main window caption into the buffer


// and append " [View 1]".
strcpy_s( szCaptionBase, sizeOfBuffer, (wMainWindow.*pfnwGC)() );
strcat_s( szCaptionBase, sizeOfBuffer, " [View 1]" );
// Set the child window's caption.
(pwChildWindow->*pfnwSC)( szCaptionBase );

Restrictions on Pointers to Members


The address of a static member isn't a pointer to a member. It's a regular pointer to the
one instance of the static member. Only one instance of a static member exists for all
objects of a given class. That means you can use the ordinary address-of (&) and
dereference (*) operators.
Pointers to Members and Virtual Functions
Invoking a virtual function through a pointer-to-member function works as if the
function had been called directly. The correct function is looked up in the v-table and
invoked.

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;
}

class Derived : public Base


{
public:
void Print(); // Print is still a virtual function.
};

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

called. Static member functions don't have a this pointer.

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 );

can be interpreted as:

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++

void Date::setMonth( int mn )


{
month = mn; // These three statements
this->month = mn; // are equivalent
(*this).month = mn;
}

The expression *this is commonly used to return the current object from a member
function:

C++

return *this;

The this pointer is also used to guard against self-reference:

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 .

Occasionally, the this pointer is used directly—for example, to manipulate self-


referential data structures, where the address of the current object is required.

Example
C++

// this_pointer.cpp
// compile with: /EHsc

#include <iostream>
#include <string.h>

using namespace std;

class Buf
{
public:
Buf( char* szBuffer, size_t sizeOfBuffer );
Buf& operator=( const Buf & );
void Display() { cout << buffer << endl; }
private:
char* buffer;
size_t sizeOfBuffer;
};

Buf::Buf( char* szBuffer, size_t sizeOfBuffer )


{
sizeOfBuffer++; // account for a NULL terminator

buffer = new char[ sizeOfBuffer ];


if (buffer)
{
strcpy_s( buffer, sizeOfBuffer, szBuffer );
sizeOfBuffer = sizeOfBuffer;
}
}

Buf& Buf::operator=( const Buf &otherbuf )


{
if( &otherbuf != this )
{
if (buffer)
delete [] buffer;

sizeOfBuffer = strlen( otherbuf.buffer ) + 1;


buffer = new char[sizeOfBuffer];
strcpy_s( buffer, sizeOfBuffer, otherbuf.buffer );
}
return *this;
}

int main()
{
Buf myBuf( "my buffer", 10 );
Buf yourBuf( "your buffer", 12 );

// Display 'my buffer'


myBuf.Display();

// assignment operator
myBuf = yourBuf;

// Display 'your buffer'


myBuf.Display();
}

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:

[ cv-qualifier-list ] class-type * const this

The member function's declarator determines cv-qualifier-list . It can be const or


volatile (or both). class-type is the name of the class.

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

void Func() myClass *

void Func() const const myClass *

void Func() volatile volatile myClass *

void Func() const volatile const volatile myClass *

The following table explains more about const and `volatile``.

Semantics of this modifiers

ノ 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

Constructors and destructors can't be declared as const or volatile . They can,


however, be invoked on const or volatile objects.

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.

The following example declares a structure that contains bit fields:

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.

END Microsoft Specific

If the declaration of a structure includes an unnamed field of length 0, as shown in the


following example:

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)
};

Then the memory layout is as shown in the following figure:

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:

Taking the address of a bit field.

Initializing a non- const reference with a bit field.

See also
Classes and Structs

You might also like