关键字this
关键字this
表示指向正在执行其成员函数的对象的指针。它在类的成员函数中用于引用对象本身。
它的一个用途是检查传递给成员函数的参数是否是对象本身。例如:
// example on this
#include <iostream>
using namespace std;
class Dummy {
public:
bool isitme (Dummy& param);
};
bool Dummy::isitme (Dummy& param)
{
if (¶m == 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;
}
它也经常用于operator=
通过引用返回对象的成员函数。以下是前面看到的笛卡尔向量的例子,它的operator=
函数可以定义为:
CVector& CVector::operator= (const CVector& param)
{
x=param.x;
y=param.y;
return *this;
}
实际上,this
函数与编译器为类隐式生成的operator=
重载操作符代码一致。
static members 静态成员
类可以包含静态成员,数据或函数。
类的静态数据成员也称为“类变量”,因为同一类的所有对象只有一个公共变量,共享相同的值:,其值与这个类的一个对象之间没有区别。
例如,它可以用于类中的一个变量,该变量可以包含一个计数器,计数器中包含该类当前分配的对象的数量,如下例所示:
// 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';
Dummy * c = new Dummy;
cout << Dummy::n << '\n';
delete c;
return 0;
}
实际上,静态成员具有与非成员变量相同的属性,但是它们具有类作用域。因此,为了避免多次声明它们,不能在类中直接初始化它们,但需要在类之外的某个地方初始化它们。如前一个例子所示:
int Dummy::n=0;
因为它是同一个类的所有对象的通用变量值,所以它可以被引用为该类的任何对象的成员,甚至可以直接引用类名(当然,这只对静态成员有效):
cout << a.n;
cout << Dummy::n;
以上两个调用引用的是同一个变量: 所有对象共享Dummy
类中的静态变量n。
同样,它类似于非成员变量,但其名称需要像类(或对象)的成员那样被访问。
类也可以有静态成员函数。它们表示的是相同的:类的成员对该类的所有对象都是公共的,完全充当非成员函数,但像类的成员一样被访问。因为它们类似于非成员函数,所以它们不能访问类的非静态成员(既不是成员变量,也不是成员函数)。他们都不能使用关键字this
。
Const成员函数
当类的对象被限定为const对象时:
const MyClass myobject;
从类外部对其数据成员的访问被限制为只读,就好像它的所有数据成员都是const
从类外部访问它们的那些成员一样。但请注意,构造函数仍然可被调用,并且允许初始化和修改这些数据成员:
// constructor on const object
#include <iostream>
using namespace std;
class MyClass {
public:
int x;
MyClass(int val) : x(val) {}
int get() {return x;}
};
int main() {
const MyClass foo(10);
// foo.x = 20; // not valid: x cannot be modified
cout << foo.x << '\n'; // ok: data member x can be read
return 0;
}
const
只有当对象的成员函数被指定为const
成员时,才能调用它们的成员函数; 在上面的示例中,无法从中调用成员get(未指定为const)foo。要指定成员是const成员,const关键字应遵循函数原型,在其参数的右括号之后:
int get() const {return x;}
请注意,const
可用于限定成员函数返回的类型。修饰返回类型的const
与指定成员的那个const
不同。两者都是独立的,位于函数原型的不同位置:
int get() const {return x;} // const member function
const int& get() {return x;} // member function returning a const&
const int& get() const {return x;} // const member function returning a const&
指定的成员函数const
不能修改非静态数据成员,也不能调用其他非const
成员函数。实质上,const
成员不得修改对象的状态。
const
对象仅限于访问标记为的成员函数const
,但非const
对象不受限制,因此可以同时访问const
非const
成员函数和非成员函数。
您可能认为无论如何您很少会声明const
对象,因此标记所有不将对象修改为const
的成员都不值得努力,但const
对象实际上非常常见。以类为参数的大多数函数实际上都是通过const
引用来获取它们,因此,这些函数只能访问它们的const
成员:
// const objects
#include <iostream>
using namespace std;
class MyClass {
int x;
public:
MyClass(int val) : x(val) {}
const int& get() const {return x;}
};
void print (const MyClass& arg) {
cout << arg.get() << '\n';
}
int main() {
MyClass foo (10);
print(foo);
return 0;
}
如果在此示例get
中未指定为const
成员,在print
函数中无法调用arg.get()
,因为const
对象只能访问const
成员函数。
成员函数可以在其使用非const
进行重载:即一个类可能有两个具有相同签名的成员函数,一个是const
修饰,而另一个不是const
修饰:在这种情况下,const
修饰的函数仅当对象本身是const
时调用,而非const
修饰的函数是当对象本身不是时调用。
// overloading members on constness
#include <iostream>
using namespace std;
class MyClass {
int x;
public:
MyClass(int val) : x(val) {}
const int& get() const {return x;}
int& get() {return x;}
};
int main() {
MyClass foo (10);
const MyClass bar (20);
foo.get() = 15; // ok: get() returns int&
// bar.get() = 25; // not valid: get() returns const int&
cout << foo.get() << '\n';
cout << bar.get() << '\n';
return 0;
}
类模板
就像我们可以创建函数模板一样,我们也可以创建类模板,允许类拥有使用模板参数作为类型的成员。例如:
template <class T>
class mypair {
T values [2];
public:
mypair (T first, T second)
{
values[0]=first; values[1]=second;
}
};
我们刚刚定义的类用于存储任何有效类型的两个元素。例如,如果我们想要声明这个类的对象来存储两个类型int的值为115和36的整数值,我们会写:
mypair<int> myobject (115, 36);
同一个类也可用于创建存储任何其他类型的对象,例如:
mypair<double> myfloats (3.0, 2.18);
构造函数是前一个类模板中唯一的成员函数,它已在类定义本身内联定义。如果成员函数是在类模板的定义之外定义的,那么它前面应该有template <...>
前缀:
// class templates
#include <iostream>
using namespace std;
template <class T>
class mypair {
T a, b;
public:
mypair (T first, T second)
{a=first; b=second;}
T getmax ();
};
template <class T>
T mypair<T>::getmax ()
{
T retval;
retval = a>b? a : b;
return retval;
}
int main () {
mypair <int> myobject (100, 75);
cout << myobject.getmax();
return 0;
}
注意上面成员函数定义的语法getmax:
template <class T>
T mypair<T>::getmax ()
模板特殊化
当特定类型作为模板参数传递时,可以为模板定义不同的实现。这称为模板特殊化。
例如,让我们假设我们有一个非常简单的类mycontainer
,它可以存储任何类型的一个元素,并且只有一个成员函数increase
,会增加它的值。但是我们发现,当它存储char
类型的元素时,使用函数成员uppercase
的完全不同的实现会更方便,因此我们决定为该类型声明一个类模板特殊化:
// template specialization
#include <iostream>
using namespace std;
// class template:
template <class T>
class mycontainer {
T element;
public:
mycontainer (T arg) {element=arg;}
T increase () {return ++element;}
};
// class template specialization:
template <>
class mycontainer <char> {
char element;
public:
mycontainer (char arg) {element=arg;}
char uppercase ()
{
if ((element>='a')&&(element<='z'))
element+='A'-'a';
return element;
}
};
int main () {
mycontainer<int> myint (7);
mycontainer<char> mychar ('j');
cout << myint.increase() << endl;
cout << mychar.uppercase() << endl;
return 0;
}
这是用于类模板特殊化的语法:
template <> class mycontainer <char> { ... };
首先,请注意我们在类名前面加上template<>
,包括一个空参数列表。这是因为所有类型都是已知的,并且此特殊化不需要模板参数,但仍然是类模板的特化,因此需要注意。
但是比这个前缀更重要的是<char>
类模板名称后面的指定的参数。此特殊化的参数本身标识模板类专用的类型char
。请注意泛型类模板和特殊化之间的差异:
template <class T> class mycontainer { ... };
template <> class mycontainer <char> { ... };
第一行是泛型模板,第二行是模板特殊化。
当我们声明模板类的特殊化时,我们还必须定义它的所有成员,甚至是那些与泛型模板类相同的成员,因为没有从泛型模板到模板特殊化成员的“继承”关系。