1.Class
Classes are an expanded concept of data structures: like data structures, they can contain data members, but they can also contain functions as members. An object is an instantiation of a class. In terms of variables, a class would be the type, and an object would be the variable. Classes are defined using either keyword class or keyword struct, with the following syntax:
class class_name {
access_specifier_1:
member1;
access_specifier_2:
member2;
...
} object_names;
Where class_name is a valid identifier for the class, object_names is an optional list of names for objects of this class. The body of the declaration can contain members, which can either be data or function declarations, and optionally access specifiers. Classes have the same format as plain data structures, except that they can also include functions and have these new things called access specifiers. An access specifier is one of the following three keywords: private, public or protected. These specifiers modify the access rights for the members that follow them:
  • private members of a class are accessible only from within other members of the same class (or from their "friends").
  • protected members are accessible from other members of the same class (or from their "friends"), but also from members of their derived classes.
  • Finally, public members are accessible from anywhere where the object is visible.
By default, all members of a class declared with the class keyword have private access for all its members. Therefore, any member that is declared before any other access specifier has private access automatically. For example:
class Rectangle {
int width, height;
public:
void set_values (int,int);
int area (void);
} rect;
Declares a class (i.e., a type) called Rectangle and an object (i.e., a variable) of this class, called rect. This class contains four members: two data members of type int (member width and member height) with private access (because private is the default access level) and two member functions with public access: the functions set_values and area, of which for now we have only included their declaration, but not their definition. Notice the difference between the class name and the object name: In the previous example, Rectangle was the class name (i.e., the type), whereas rect was an object of type Rectangle. It is the same relationship int and a have in the following declaration:
int a;
where int is the type name (the class) and a is the variable name (the object). After the declarations of Rectangle and rect, any of the public members of object rect can be accessed as if they were normal functions or normal variables, by simply inserting a ** dot (.) ** between object name and member name. This follows the same syntax as accessing the members of plain data structures. For example:
rect.set_values (3,4);
myarea = rect.area();
The only members of rect that cannot be accessed from outside the class are width and height, since they have private access and they can only be referred to from within other members of that same class. Here is the complete example of class Rectangle:
// classes example
#include <iostream>
using namespace std;

class Rectangle {
int width, height;
public:
void set_values (int,int);
int area() {return width*height;}
};

void Rectangle::set_values (int x, int y) {
width = x;
height = y;
}

int main () {
Rectangle rect;
rect.set_values (3,4);
cout << "area: " << rect.area();
return 0;
}
This example reintroduces the scope operator (::, two colons), seen in earlier chapters in relation to namespaces. Here it is used in the definition of function set_values to define a member of a class outside the class itself. Notice that the definition of the member function area has been included directly within the definition of class Rectangle given its extreme simplicity. Conversely, set_values it is merely declared with its prototype within the class, but its definition is outside it. In this outside definition, the operator of scope (::) is used to specify that the function being defined is a member of the class Rectangle and not a regular non-member function. The scope operator (::) specifies the class to which the member being defined belongs, granting exactly the same scope properties as if this function definition was directly included within the class definition. For example, the function set_values in the previous example has access to the variables width and height, which are private members of class Rectangle, and thus only accessible from other members of the class, such as this. The only difference between defining a member function completely within the class definition or to just include its declaration in the function and define it later outside the class, is that in the first case the function is automatically considered an inline member function by the compiler, while in the second it is a normal (not-inline) class member function. This causes no differences in behavior, but only on possible compiler optimizations. Members width and height have private access (remember that if nothing else is specified, all members of a class defined with keyword class have private access). By declaring them private, access from outside the class is not allowed. This makes sense, since we have already defined a member function to set values for those members within the object: the member function set_values. Therefore, the rest of the program does not need to have direct access to them. Perhaps in a so simple example as this, it is difficult to see how restricting access to these variables may be useful, but in greater projects it may be very important that values cannot be modified in an unexpected way (unexpected from the point of view of the object). The most important property of a class is that it is a type, and as such, we can declare multiple objects of it. For example, following with the previous example of class Rectangle, we could have declared the object rectb in addition to object rect:
// example: one class, two objects
#include <iostream>
using namespace std;

class Rectangle {
int width, height;
public:
void set_values (int,int);
int area () {return width*height;}
};

void Rectangle::set_values (int x, int y) {
width = x;
height = y;
}

int main () {
Rectangle rect, rectb;
rect.set_values (3,4);
rectb.set_values (5,6);
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;
return 0;
}
In this particular case, the class (type of the objects) is Rectangle, of which there are two instances (i.e., objects): rect and rectb. Each one of them has its own member variables and member functions. Notice that the call to rect.area() does not give the same result as the call to rectb.area(). This is because each object of class Rectangle has its own variables width and height, as they -in some way- have also their own function members set_value and area that operate on the object's own member variables. Classes allow programming using object-oriented paradigms: Data and functions are both members of the object, reducing the need to pass and carry handlers or other state variables as arguments to functions, because they are part of the object whose member is called. Notice that no arguments were passed on the calls to rect.area or rectb.area. Those member functions directly used the data members of their respective objects rect and rectb.
总结: 1)类的语法形式有,关键字class,成员(成员变量,成员函数),成员修饰符; 2)类可以定义多个实例(对象);对象通过,点,调用成员 3)可以在类外定义成员函数,通过::,来确定类外函数的归属;
2.Constructors
What would happen in the previous example if we called the member function area before having called set_values? An undetermined result, since the members width and height had never been assigned a value. In order to avoid that, a class can include a special function called its constructor, which is automatically called whenever a new object of this class is created, allowing the class to initialize member variables or allocate storage. This constructor function is declared just like a regular member function, but with a name that matches the class name and without any return type; not even void. The Rectangle class above can easily be improved by implementing a constructor:
// example: class constructor
#include <iostream>
using namespace std;

class Rectangle {
int width, height;
public:
Rectangle (int,int);
int area () {return (width*height);}
};

Rectangle::Rectangle (int a, int b) {
width = a;
height = b;
}

int main () {
Rectangle rect (3,4);
Rectangle rectb (5,6);
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;
return 0;
}
The results of this example are identical to those of the previous example. But now, class Rectangle has no member function set_values, and has instead a constructor that performs a similar action: it initializes the values of width and height with the arguments passed to it. Notice how these arguments are passed to the constructor at the moment at which the objects of this class are created:
Rectangle rect (3,4);
Rectangle rectb (5,6);
Constructors cannot be called explicitly as if they were regular member functions. They are only executed once, when a new object of that class is created. Notice how neither the constructor prototype declaration (within the class) nor the latter constructor definition, have return values; not even void: Constructors never return values, they simply initialize the object.
总结: 1)构造函数与普通函数形式一致,只是函数名称与类相同 2)构造函数用于初始化成员变量,新建类对象时,会自动初始化成员对象,很方便
3.Overloading constructors
Like any other function, a constructor can also be overloaded with different versions taking different parameters: with a different number of parameters and/or parameters of different types. The compiler will automatically call the one whose parameters match the arguments:
// overloading class constructors
#include <iostream>
using namespace std;

class Rectangle {
int width, height;
public:
Rectangle ();
Rectangle (int,int);
int area (void) {return (width*height);}
};

Rectangle::Rectangle () {
width = 5;
height = 5;
}

Rectangle::Rectangle (int a, int b) {
width = a;
height = b;
}

int main () {
Rectangle rect (3,4);
Rectangle rectb;
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;
return 0;
}
In the above example, two objects of class Rectangle are constructed: rect and rectb. rect is constructed with two arguments, like in the example before. But this example also introduces a special kind constructor: the default constructor. The default constructor is the constructor that takes no parameters, and it is special because it is called when an object is declared but is not initialized with any arguments. In the example above, the default constructor is called for rectb. Note how rectb is not even constructed with an empty set of parentheses - in fact, empty parentheses cannot be used to call the default constructor:
Rectangle rectb;   // ok, default constructor called
Rectangle rectc(); // oops, default constructor NOT called
This is because the empty set of parentheses would make of rectc a function declaration instead of an object declaration: It would be a function that takes no arguments and returns a value of type Rectangle.
总结: 1)构造函数可以重载,调用时可以根据其函数参数的类型或者参数的个数系统自动区分 2)默认构造函数会初始化class的成员,其在创建对象调用时,不需要加括号,因为它没有参数,系统省略了函数的括号表达形式
4.Uniform initialization
The way of calling constructors by enclosing their arguments in parentheses, as shown above, is known as functional form. But constructors can also be called with other syntaxes: First, constructors with a single parameter can be called using the variable initialization syntax (an equal sign followed by the argument): class_name object_name = initialization_value; More recently, C++ introduced the possibility of constructors to be called using uniform initialization, which essentially is the same as the functional form, but using braces ({}) instead of parentheses (()): class_name object_name { value, value, value, ... } Optionally, this last syntax can include an equal sign before the braces. Here is an example with four ways to construct objects of a class whose constructor takes a single parameter:
// classes and uniform initialization
#include <iostream>
using namespace std;

class Circle {
double radius;
public:
Circle(double r) { radius = r; }
double circum() {return 2*radius*3.14159265;}
};

int main () {
Circle foo (10.0);   // functional form
Circle bar = 20.0;   // assignment init.
Circle baz {30.0};   // uniform init.
Circle qux = {40.0}; // POD-like

cout << "foo's circumference: " << foo.circum() << '\n';
return 0;
}
An advantage of uniform initialization over functional form is that, unlike parentheses, braces cannot be confused with function declarations, and thus can be used to explicitly call default constructors:
Rectangle rectb;   // default constructor called
Rectangle rectc(); // function declaration (default constructor NOT called)
Rectangle rectd{}; // default constructor called 
The choice of syntax to call constructors is largely a matter of style. Most existing code currently uses functional form, and some newer style guides suggest to choose uniform initialization over the others, even though it also has its potential pitfalls for its preference of initializer_list as its type.
总结: 1)构造函数有三种表达形式,无参(),有参,无参{}
5.Member initialization in constructors
When a constructor is used to initialize other members, these other members can be initialized directly, without resorting to statements in its body. This is done by inserting, before the constructor's body, a colon (:) and a list of initializations for class members. For example, consider a class with the following declaration:
class Rectangle {
int width,height;
public:
Rectangle(int,int);
int area() {return width*height;}
};
The constructor for this class could be defined, as usual, as:
Rectangle::Rectangle (int x, int y) { width=x; height=y; }
But it could also be defined using member initialization as:
Rectangle::Rectangle (int x, int y) : width(x) { height=y; }
Or even:
Rectangle::Rectangle (int x, int y) : width(x), height(y) { }
Note how in this last case, the constructor does nothing else than initialize its members, hence it has an empty function body. For members of fundamental types, it makes no difference which of the ways above the constructor is defined, because they are not initialized by default, but for member objects (those whose type is a class), if they are not initialized after the colon, they are default-constructed. Default-constructing all members of a class may or may always not be convenient: in some cases, this is a waste (when the member is then reinitialized otherwise in the constructor), but in some other cases, default-construction is not even possible (when the class does not have a default constructor). In these cases, members shall be initialized in the member initialization list. For example:
// member initialization
#include <iostream>
using namespace std;

class Circle {
double radius;
public:
Circle(double r) : radius(r) { }
double area() {return radius*radius*3.14159265;}
};

class Cylinder {
Circle base;
double height;
public:
Cylinder(double r, double h) : base (r), height(h) {}
double volume() {return base.area() * height;}
};

int main () {
Cylinder foo (10,20);

cout << "foo's volume: " << foo.volume() << '\n';
return 0;
}
In this example, class Cylinder has a member object whose type is another class (base's type is Circle). Because objects of class Circle can only be constructed with a parameter, Cylinder's constructor needs to call base's constructor, and the only way to do this is in the member initializer list. These initializations can also use uniform initializer syntax, using braces {} instead of parentheses ():
Cylinder::Cylinder (double r, double h) : base{r}, height{h} { }
总结: 构造函数的另外的表达方式;感觉有一点复杂,将语法复杂化了;需要记住构造函数在创建对象时调用,构造函数的函数名为当前类名
6.Pointers to classes
Objects can also be pointed to by pointers: Once declared, a class becomes a valid type, so it can be used as the type pointed to by a pointer. For example:
Rectangle * prect;
is a pointer to an object of class Rectangle. Similarly as with plain data structures, the members of an object can be accessed directly from a pointer by using the arrow operator (->). Here is an example with some possible combinations:
// pointer to classes example
#include <iostream>
using namespace std;

class Rectangle {
int width, height;
public:
Rectangle(int x, int y) : width(x), height(y) {}
int area(void) { return width * height; }
};


int main() {
Rectangle obj (3, 4);
Rectangle * foo, * bar, * baz;
foo = &obj;
bar = new Rectangle (5, 6);
baz = new Rectangle[2] { {2,5}, {3,6} };
cout << "obj's area: " << obj.area() << '\n';
cout << "*foo's area: " << foo->area() << '\n';
cout << "*bar's area: " << bar->area() << '\n';
cout << "baz[0]'s area:" << baz[0].area() << '\n';
cout << "baz[1]'s area:" << baz[1].area() << '\n';       
delete bar;
delete[] baz;
return 0;
}
This example makes use of several operators to operate on objects and pointers (operators *, &, ., ->, []). They can be interpreted as: Most of these expressions have been introduced in earlier chapters. Most notably, the chapter about arrays introduced the offset operator ([]) and the chapter about plain data structures introduced the arrow operator (->).
总结: 介绍类创建一个指针对象的使用语法;
7.Classes defined with struct and union
Classes can be defined not only with keyword class, but also with keywords struct and union. The keyword struct, generally used to declare plain data structures, can also be used to declare classes that have member functions, with the same syntax as with keyword class. The only difference between both is that members of classes declared with the keyword struct have public access by default, while members of classes declared with the keyword class have private access by default. For all other purposes both keywords are equivalent in this context. Conversely, the concept of unions is different from that of classes declared with struct and class, since unions only store one data member at a time, but nevertheless they are also classes and can thus also hold member functions. The default access in union classes is public.
8.Overloading operators
Classes, essentially, define new types to be used in C++ code. And types in C++ not only interact with code by means of constructions and assignments. They also interact by means of operators. For example, take the following operation on fundamental types:
int a, b, c;
a = b + c;
Here, different variables of a fundamental type (int) are applied the addition operator, and then the assignment operator. For a fundamental arithmetic type, the meaning of such operations is generally obvious and unambiguous, but it may not be so for certain class types. For example:
struct myclass {
string product;
float price;
} a, b, c;
a = b + c;
Here, it is not obvious what the result of the addition operation on b and c does. In fact, this code alone would cause a compilation error, since the type myclass has no defined behavior for additions. However, C++ allows most operators to be overloaded so that their behavior can be defined for just about any type, including classes. Here is a list of all the operators that can be overloaded: Operators are overloaded by means of operator functions, which are regular functions with special names: their name begins by the operator keyword followed by the operator sign that is overloaded. The syntax is: type operator sign (parameters) { /... body .../ } For example, cartesian vectors are sets of two coordinates: x and y. The addition operation of two cartesian vectors is defined as the addition both x coordinates together, and both y coordinates together. For example, adding the cartesian vectors (3,1) and (1,2) together would result in (3+1,1+2) = (4,3). This could be implemented in C++ with the following code:
// overloading operators example
#include <iostream>
using namespace std;

class CVector {
public:
int x,y;
CVector () {};
CVector (int a,int b) : x(a), y(b) {}
CVector operator + (const CVector&);
};

CVector CVector::operator+ (const CVector& param) {
CVector temp;
temp.x = x + param.x;
temp.y = y + param.y;
return temp;
}

int main () {
CVector foo (3,1);
CVector bar (1,2);
CVector result;
result = foo + bar;
cout << result.x << ',' << result.y << '\n';
return 0;
}
If confused about so many appearances of CVector, consider that some of them refer to the class name (i.e., the type) CVector and some others are functions with that name (i.e., constructors, which must have the same name as the class). For example:
CVector (int, int) : x(a), y(b) {}  // function name CVector (constructor)
CVector operator+ (const CVector&); // function that returns a CVector
The function operator+ of class CVector overloads the addition operator (+) for that type. Once declared, this function can be called either implicitly using the operator, or explicitly using its functional name:
c = a + b;
c = a.operator+ (b);
Both expressions are equivalent. The operator overloads are just regular functions which can have any behavior; there is actually no requirement that the operation performed by that overload bears a relation to the mathematical or usual meaning of the operator, although it is strongly recommended. For example, a class that overloads operator+ to actually subtract or that overloads operator== to fill the object with zeros, is perfectly valid, although using such a class could be challenging. The parameter expected for a member function overload for operations such as operator+ is naturally the operand to the right hand side of the operator. This is common to all binary operators (those with an operand to its left and one operand to its right). But operators can come in diverse forms. Here you have a table with a summary of the parameters needed for each of the different operators than can be overloaded (please, replace @ by the operator in each case): Where a is an object of class A, b is an object of class B and c is an object of class C. TYPE is just any type (that operators overloads the conversion to type TYPE). Notice that some operators may be overloaded in two forms: either as a member function or as a non-member function: The first case has been used in the example above for operator+. But some operators can also be overloaded as non-member functions; In this case, the operator function takes an object of the proper class as first argument. For example:
// non-member operator overloads
#include <iostream>
using namespace std;

class CVector {
public:
int x,y;
CVector () {}
CVector (int a, int b) : x(a), y(b) {}
};


CVector operator+ (const CVector& lhs, const CVector& rhs) {
CVector temp;
temp.x = lhs.x + rhs.x;
temp.y = lhs.y + rhs.y;
return temp;
}

int main () {
CVector foo (3,1);
CVector bar (1,2);
CVector result;
result = foo + bar;
cout << result.x << ',' << result.y << '\n';
return 0;
}
总结: 1.C++允许运算符重载,如加号,不仅可以表示整数相加,也可以表示字符串类型链接,或者是对象相加; 2. 问题:如果连续的运算,有整数相加,字符串相连,对象相加,其是根据操作符两边的对象类型,自动判断采用哪一个功能的运算符吗?
9.The keyword this
The keyword this represents a pointer to the object whose member function is being executed. It is used within a class's member function to refer to the object itself. One of its uses can be to check if a parameter passed to a member function is the object itself. For example:
// example on this
#include <iostream>
using namespace std;

class Dummy {
public:
bool isitme (Dummy& param);
};

bool Dummy::isitme (Dummy& param)
{
if (&param == this) return true;
else return false;
}

int main () {
Dummy a;
Dummy* b = &a;
if ( b->isitme(a) )
cout << "yes, &a is b\n";
return 0;
}
It is also frequently used in operator= member functions that return objects by reference. Following with the examples on cartesian vector seen before, its operator= function could have been defined as:
CVector& CVector::operator= (const CVector& param)
{
x=param.x;
y=param.y;
return *this;

In fact, this function is very similar to the code that the compiler generates implicitly for this class for operator=. this是指向实例化对象本身时候的一个指针,里面存储的是对象本身的地址,通过该地址可以访问内部的成员函数和成员变量。 为什么需要this?因为this作用域是在类的内部,自己声明一个类的时候,还不知道实例化对象的名字,所以用this来使用对象变量的自身。在非静态成员函数中,编译器在编译的时候加上this作为隐含形参,通过this来访问各个成员(即使你没有写上this指针)。例如a.fun(1)<==等价于==>fun(&a,1) this的使用:1)在类的非静态成员函数中返回对象的本身时候,直接用return *this(常用于操作符重载和赋值、拷贝等函数)。2)传入函数的形参与成员变量名相同时,例如:this->n = n (不能写成n=n) this 是 C++ 中的一个关键字,也是一个 const 指针,它指向当前对象,通过它可以访问当前对象的所有成员。所谓当前对象,是指正在使用的对象。this 只能用在类的内部,通过 this 可以访问类的所有成员,包括 private、protected、public 属性的。
10.Static members
A class can contain static members, either data or functions. A static data member of a class is also known as a "class variable", because there is only one common variable for all the objects of that same class, sharing the same value: i.e., its value is not different from one object of this class to another. For example, it may be used for a variable within a class that can contain a counter with the number of objects of that class that are currently allocated, as in the following example:
// static members in classes
#include <iostream>
using namespace std;

class Dummy {
public:
static int n;
Dummy () { n++; };
};

int Dummy::n=0;

int main () {
Dummy a;
Dummy b[5];
cout << a.n << '\n';///6
Dummy * c = new Dummy;
cout << Dummy::n << '\n';//7
delete c;
return 0;
}
In fact, static members have the same properties as non-member variables but they enjoy class scope. For that reason, and to avoid them to be declared several times, they cannot be initialized directly in the class, but need to be initialized somewhere outside it. As in the previous example:
int Dummy::n=0;
Because it is a common variable value for all the objects of the same class, it can be referred to as a member of any object of that class or even directly by the class name (of course this is only valid for static members):
cout << a.n;
cout << Dummy::n;
These two calls above are referring to the same variable: the static variable n within class Dummy shared by all objects of this class. Again, it is just like a non-member variable, but with a name that requires to be accessed like a member of a class (or an object). Classes can also have static member functions. These represent the same: members of a class that are common to all object of that class, acting exactly as non-member functions but being accessed like members of the class. Because they are like non-member functions, they cannot access non-static members of the class (neither member variables nor member functions). They neither can use the keyword this. 对象的内存中包含了成员变量,不同的对象占用不同的内存,这使得不同对象的成员变量相互独立,它们的值不受其他对象的影响。例如有两个相同类型的对象 a、b,它们都有一个成员变量 m_name,那么修改 a.m_name 的值不会影响 b.m_name 的值。 可是有时候我们希望在多个对象之间共享数据,对象 a 改变了某份数据后对象 b 可以检测到。共享数据的典型使用场景是计数,以前面的 Student 类为例,如果我们想知道班级中共有多少名学生,就可以设置一份共享的变量,每次创建对象时让该变量加 1。 在C++中,我们可以使用静态成员变量来实现多个对象共享数据的目标。静态成员变量是一种特殊的成员变量,它被关键字static修饰,例如:
class Student{
public:
Student(char *name, int age, float score);
void show();
public:
static int m_total;  //静态成员变量
private:
char *m_name;
int m_age;
float m_score;
};
这段代码声明了一个静态成员变量 m_total,用来统计学生的人数。 static 成员变量属于类,不属于某个具体的对象,即使创建多个对象,也只为 m_total 分配一份内存,所有对象使用的都是这份内存中的数据。当某个对象修改了 m_total,也会影响到其他对象。 static 成员变量必须在类声明的外部初始化,具体形式为:
type class::name = value;
type 是变量的类型,class 是类名,name 是变量名,value 是初始值。将上面的 m_total 初始化:
int Student::m_total = 0;
静态成员变量在初始化时不能再加 static,但必须要有数据类型。被 private、protected、public 修饰的静态成员变量都可以用这种方式初始化。 注意:static 成员变量的内存既不是在声明类时分配,也不是在创建对象时分配,而是在(类外)初始化时分配。反过来说,没有在类外初始化的 static 成员变量不能使用。 static 成员变量既可以通过对象来访问,也可以通过类来访问。请看下面的例子:
//通过类类访问 static 成员变量
Student::m_total = 10;
//通过对象来访问 static 成员变量
Student stu("小明", 15, 92.5f);
stu.m_total = 20;
//通过对象指针来访问 static 成员变量
Student *pstu = new Student("李华", 16, 96);
pstu -> m_total = 20;
这三种方式是等效的。 注意:static 成员变量不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问。具体来说,static 成员变量和普通的 static 变量类似,都在内存分区中的全局数据区分配内存,不了解的读者请阅读《C语言内存精讲》专题。 下面来看一个完整的例子:
#include <iostream>
using namespace std;

class Student{
public:
Student(char *name, int age, float score);
void show();
private:
static int m_total;  //静态成员变量
private:
char *m_name;
int m_age;
float m_score;
};

//初始化静态成员变量
int Student::m_total = 0;

Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){
m_total++;  //操作静态成员变量
}
void Student::show(){
cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<"(当前共有"<<m_total<<"名学生)"<<endl;
}

int main(){
//创建匿名对象
(new Student("小明", 15, 90)) -> show();
(new Student("李磊", 16, 80)) -> show();
(new Student("张华", 16, 99)) -> show();
(new Student("王康", 14, 60)) -> show();

return 0;
}
运行结果: 小明的年龄是15,成绩是90(当前共有1名学生) 李磊的年龄是16,成绩是80(当前共有2名学生) 张华的年龄是16,成绩是99(当前共有3名学生) 王康的年龄是14,成绩是60(当前共有4名学生) 本例中将 m_total 声明为静态成员变量,每次创建对象时,会调用构造函数使 m_total 的值加 1。 之所以使用匿名对象,是因为每次创建对象后只会使用它的 show() 函数,不再进行其他操作。不过使用匿名对象无法回收内存,会导致内存泄露,在中大型程序中不建议使用。 几点说明 1)一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量,都可以引用它。 2)static 成员变量和普通 static 变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放。这就意味着,static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。而普通成员变量在对象创建时分配内存,在对象销毁时释放内存。 3)静态成员变量必须初始化,而且只能在类体外进行。例如:
int Student::m_total = 10;
初始化时可以赋初值,也可以不赋值。如果不赋值,那么会被默认初始化为 0。全局数据区的变量都有默认的初始值 0,而动态数据区(堆区、栈区)变量的默认值是不确定的,一般认为是垃圾值。 4)静态成员变量既可以通过对象名访问,也可以通过类名访问,但要遵循 private、protected 和 public 关键字的访问权限限制。当通过对象名访问时,对于不同的对象,访问的是同一份内存。