Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CPlusPlusNotesForProfessionals.pdf
Скачиваний:
58
Добавлен:
20.05.2023
Размер:
5.11 Mб
Скачать

//Calls BaseOuter::BaseInner_::do_something(); BaseOuter* b = new BaseOuter; BaseOuter::Inner& bin = b->getInner(); bin.do_something(); b->getInner().do_something();

//Calls DerivedOuter::DerivedInner_::do_something(); BaseOuter* d = new DerivedOuter;

BaseOuter::Inner& din = d->getInner(); din.do_something(); d->getInner().do_something();

In the above case, both BaseOuter and DerivedOuter supply the member type Inner, as BaseInner_ and DerivedInner_, respectively. This allows nested types to be derived without breaking the enclosing class' interface, and allows the nested type to be used polymorphically.

Section 34.11: Unnamed struct/class

Unnamed struct is allowed (type has no name)

void foo()

{

struct /* No name */ { float x;

float y; } point;

point.x = 42;

}

or

struct Circle

{

struct /* No name */ { float x;

float y;

} center; // but a member name float radius;

};

and later

Circle circle; circle.center.x = 42.f;

but NOT anonymous struct (unnamed type and unnamed object)

struct InvalidCircle

{

struct /* No name */ { float centerX; float centerY;

}; // No member either. float radius;

};

Note: Some compilers allow anonymous struct as extension.

GoalKicker.com – C++ Notes for Professionals

186

Version ≥ C++11

lamdba can be seen as a special unnamed struct.

decltype allows to retrieve the type of unnamed struct:

decltype(circle.point) otherPoint;

unnamed struct instance can be parameter of template method:

void print_square_coordinates()

{

const struct {float x; float y;} points[] = { {-1, -1}, {-1, 1}, {1, -1}, {1, 1}

};

// for range relies on `template <class T, std::size_t N> std::begin(T (&)[N])` for (const auto& point : points) {

std::cout << "{" << point.x << ", " << point.y << "}\n";

}

decltype(points[0]) topRightCorner{1, 1};

auto it = std::find(points, points + 4, topRightCorner); std::cout << "top right corner is the "

<< 1 + std::distance(points, it) << "th\n";

}

Section 34.12: Static class members

A class is also allowed to have static members, which can be either variables or functions. These are considered to be in the class' scope, but aren't treated as normal members; they have static storage duration (they exist from the start of the program to the end), aren't tied to a particular instance of the class, and only one copy exists for the entire class.

class Example {

 

 

static int num_instances;

// Static data member (static member variable).

 

int i;

// Non-static member variable.

public:

 

 

static std::string static_str;

// Static data member (static member variable).

 

static int static_func();

// Static member function.

 

// Non-static member functions can modify static member variables.

 

Example() { ++num_instances; }

 

 

void set_str(const std::string& str);

};

 

 

int

Example::num_instances;

 

std::string Example::static_str = "Hello.";

// ...

Example one, two, three;

//Each Example has its own "i", such that:

//(&one.i != &two.i)

//(&one.i != &three.i)

//(&two.i != &three.i).

//All three Examples share "num_instances", such that:

GoalKicker.com – C++ Notes for Professionals

187

//(&one.num_instances == &two.num_instances)

//(&one.num_instances == &three.num_instances)

//(&two.num_instances == &three.num_instances)

Static member variables are not considered to be defined inside the class, only declared, and thus have their definition outside the class definition; the programmer is allowed, but not required, to initialise static variables in their definition. When defining the member variables, the keyword static is omitted.

class Example {

 

static int num_instances;

// Declaration.

public:

 

static std::string static_str;

// Declaration.

// ...

 

};

 

 

 

int

Example::num_instances;

// Definition.

Zero-initialised.

std::string Example::static_str = "Hello."; // Definition.

 

Due to this, static variables can be incomplete types (apart from void), as long as they're later defined as a complete type.

struct ForwardDeclared; class ExIncomplete {

static ForwardDeclared

fd;

static ExIncomplete

i_contain_myself;

static int

an_array[];

};

 

struct ForwardDeclared {}; ForwardDeclared ExIncomplete::fd;

ExIncomplete

ExIncomplete::i_contain_myself;

int

ExIncomplete::an_array[5];

Static member functions can be defined inside or outside the class definition, as with normal member functions. As with static member variables, the keyword static is omitted when defining static member functions outside the class definition.

// For Example above, either...

class Example {

// ...

public:

static int static_func() { return num_instances; }

// ...

void set_str(const std::string& str) { static_str = str; }

};

// Or...

class Example { /* ... */ };

int Example::static_func() { return num_instances; }

GoalKicker.com – C++ Notes for Professionals

188

void Example::set_str(const std::string& str) { static_str = str; }

If a static member variable is declared const but not volatile, and is of an integral or enumeration type, it can be initialised at declaration, inside the class definition.

enum E { VAL = 5

};

 

struct ExConst {

 

 

const static

int ci = 5;

// Good.

static const

E ce = VAL;

// Good.

const static

double cd = 5;

// Error.

static const

volatile int cvi = 5;

// Error.

const static

double good_cd;

 

static const

volatile int good_cvi;

 

};

 

 

const double ExConst::good_cd = 5;

// Good.

const volatile int ExConst::good_cvi = 5; // Good.

Version ≥ C++11

As of C++11, static member variables of LiteralType types (types that can be constructed at compile time, according to constexpr rules) can also be declared as constexpr; if so, they must be initialised within the class definition.

struct ExConstexpr {

 

constexpr static int ci = 5;

// Good.

static constexpr double cd = 5;

// Good.

constexpr static int carr[] = { 1, 1, 2 };

// Good.

static constexpr ConstexprConstructibleClass c{}; // Good.

constexpr static int bad_ci;

// Error.

};

 

constexpr int ExConstexpr::bad_ci = 5;

// Still an error.

 

 

If a const or constexpr static member variable is odr-used (informally, if it has its address taken or is assigned to a reference), then it must still have a separate definition, outside the class definition. This definition is not allowed to contain an initialiser.

struct ExODR {

static const int odr_used = 5;

};

// const int ExODR::odr_used;

const int* odr_user = & ExODR::odr_used; // Error; uncomment above line to resolve.

As static members aren't tied to a given instance, they can be accessed using the scope operator, ::.

std::string str = Example::static_str;

They can also be accessed as if they were normal, non-static members. This is of historical significance, but is used less commonly than the scope operator to prevent confusion over whether a member is static or non-static.

Example ex;

std::string rts = ex.static_str;

GoalKicker.com – C++ Notes for Professionals

189

Class members are able to access static members without qualifying their scope, as with non-static class members.

class ExTwo {

static int num_instances; int my_num;

public:

ExTwo() : my_num(num_instances++) {}

static int get_total_instances() { return num_instances; } int get_instance_number() const { return my_num; }

};

int ExTwo::num_instances;

They cannot be mutable, nor would they need to be; as they aren't tied to any given instance, whether an instance is or isn't const doesn't a ect static members.

struct ExDontNeedMutable { int immuta;

mutable int muta;

static int i;

ExDontNeedMutable() : immuta(-5), muta(-5) {}

};

int ExDontNeedMutable::i;

// ...

const ExDontNeedMutable dnm;

dnm.immuta = 5; // Error: Can't modify read-only object.

dnm.muta = 5; // Good. Mutable fields of const objects can be written.

dnm.i = 5; // Good. Static members can be written regardless of an instance's const-ness.

Static members respect access modifiers, just like non-static members.

class ExAccess {

static int prv_int;

protected:

static int pro_int;

public:

static int pub_int;

};

int ExAccess::prv_int; int ExAccess::pro_int; int ExAccess::pub_int;

// ...

int x1 = ExAccess::prv_int; // Error: int ExAccess::prv_int is private. int x2 = ExAccess::pro_int; // Error: int ExAccess::pro_int is protected. int x3 = ExAccess::pub_int; // Good.

As they aren't tied to a given instance, static member functions have no this pointer; due to this, they can't access non-static member variables unless passed an instance.

GoalKicker.com – C++ Notes for Professionals

190