C/C++ 语言系列(九)面向对象编程之类、类模板、类指针
类
类的声明:
1 | class class_name { |
访问修饰符
private
members of a class are accessible only from within other members of the same class (or from theirfriend
). By default, all members of a class have private access for all its members.protected
members are accessible from other members of the same class (or from theirfriend
), but also from members of their derived classes.- Finally,
public
members are accessible from anywhere where the object is visible.
this 指针
在 C++ 中,每一个对象都能通过 this
指针来访问自己的地址。this
指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。
友元函数没有 this
指针,因为友元不是类的成员。只有成员函数才有 this
指针。
静态成员(static)
static
关键字用于修饰静态成员变量或函数。限制如下:
- 无法访问类的非静态成员变量或函数;
- 无法使用
this
指针。
静态成员的引用方式:Runoob:runoob_age
成员函数
有两种方式定义类的成员函数:
- 内联成员函数(inline member function)
- 普通成员函数(not-inline member function)
两种方式并不会导致行为上的差异,而只会导致可能的编译器优化。
1 | // classes example |
常成员函数
To specify that a member is a const
member, the const
keyword shall follow the function prototype, after the closing parenthesis for its parameters:
1 | int get() const {return x;} // const member function |
构造函数
类的构造函数是类的一种特殊的成员函数,构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void
。它会在每次创建类的新对象时执行:
1 | // 调用有参构造函数 |
构造函数重载
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:
1 | class Rectangle { |
This example 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 howrectb
is not even constructed with an empty set of parentheses - in fact, empty parentheses cannot be used to call the default constructor:
1
2 Rectangle rectb; // ok, default constructor called
Rectangle rectc(); // oops, default constructor NOT called, empty parentheses interpreted as a function declarationThis 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 typeRectangle
.
在构造函数中初始化成员变量
使用构造函数初始化其他成员变量时,有下面两种方式:
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:
1
2
3
4
5
6 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:
1 Rectangle::Rectangle(int x, int y) { width=x; height=y; }But it could also be defined using member initialization as:
1 Rectangle::Rectangle(int x, int y) : width(x), height(y) { }Or even:
1 Rectangle::Rectangle(int x, int y) : Shape(x, y) { }Note how in this last case, the constructor does nothing else than initialize its members, hence it has an empty function body.
析构函数(~)
类的析构函数是类的一种特殊的成员函数,它会在每次删除对象时执行,有助于在跳出程序前释放资源(比如关闭文件、释放内存等)。
1 | class Rectangle { |
析构函数要点:
- 析构函数名称与类的名称完全相同,前缀使用关键字
~
; - 一个类中只能声明一个析构函数(destructor cannot be redeclared);
- 析构函数无参数(destructor cannot have any parameters);
- 析构函数无返回值(destructor cannot have a return type);
- 不可重载。
友元函数(friend)
类的友元函数(friend
关键字),有权访问类的所有私有(private
)和保护(protected
)成员变量。尽管友元函数在类中声明,但是友元函数并不是类的成员函数。
1 | class Rectangle { |
友元函数破坏了类的封装性,实践中不建议使用。
重载(overload)
函数重载
可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。
运算符重载
一、支持重载的运算符:
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:
1 | + - * / = < > += -= *= /= << >> |
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:
1 | type operator sign (parameters) { /*... body ...*/ } |
二、重载运算符的不同形式:
There is 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):
Expression | Operator | Member function | Non-member function |
---|---|---|---|
@a |
+ - * & ! ~ ++ -- |
A::operator@() |
operator@(A) |
a@ |
++ -- |
A::operator@(int) |
operator@(A,int) |
a@b |
+ - * / % ^ & | < > == != <= >= << >> && || , |
A::operator@(B) |
operator@(A,B) |
a@b |
= += -= *= /= %= ^= &= |= <<= >>= [] |
A::operator@(B) |
- |
a(b,c...) |
() |
A::operator()(B,C...) |
- |
a->b |
-> |
A::operator->() |
- |
(TYPE) a |
TYPE |
A::operator TYPE() |
- |
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.
三、例子:
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:
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:
1 | // called either implicitly using the operator |
Both expressions are equivalent.
四、注意点:
Attention
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 overloadsoperator==
to fill the object with zeros, is perfectly valid, although using such a class could be challenging.
函数重定义(redefine)
即 Java 语言中的方法重写(rewrite)。
类指针
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:
1 | // 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:
1 | // pointer to classes example |
This example makes use of several operators to operate on objects and pointers (operators *
, &
, .
, ->
). They can be interpreted as:
expression | can be read as |
---|---|
*x |
pointed to by x |
&x |
address of x |
x.y |
member y of object x |
x->y |
member y of object pointed to by x |
(*x).y |
member y of object pointed to by x (equivalent to the previous one) |
模板
模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
函数模板
1 | // function templates |
类模板
Just like we can create function templates, we can also create class templates, allowing classes to have members that use template parameters as types. For example:
1 | // class templates |
Notice the syntax of the definition of member function getmax
:
1 | template <class T> |
There are three T
‘s in this declaration: The first one is the template parameter. The second T
refers to the type returned by the function. And the third T
(the one between angle brackets) is also a requirement: It specifies that this function’s template parameter is also the class template parameter.
模板类
模板类是类模板实例化后的一个产物。
It is possible to define a different implementation for a template when a specific type is passed as template argument. This is called a template specialization.
For example:
1 | // template specialization |
This is the syntax used for the class template specialization:
1 | template <> |
First of all, notice that we precede the class name with template<>
, including an empty parameter list. This is because all types are known and no template arguments are required for this specialization, but still, it is the specialization of a class template, and thus it requires to be noted as such.
But more important than this prefix, is the <char>
specialization parameter after the class template name. This specialization parameter itself identifies the type for which the template class is being specialized (char
). Notice the differences between the generic class template and the specialization:
1 | template <class T> class mycontainer { ... }; |
The first line is the generic template, and the second one is the specialization.
When we declare specializations for a template class, we must also define all its members, even those identical to the generic template class, because there is no “inheritance” of members from the generic template to the specialization.
参考
http://www.cplusplus.com/doc/tutorial/classes/
http://www.cplusplus.com/doc/tutorial/templates/
http://www.cplusplus.com/doc/tutorial/classes2/
https://www.cplusplus.com/doc/oldtutorial/templates/