Classes(基础)
1. 类的基础介绍
定义在class内部的函数默认是inline的。
1.1 this指针介绍
当我们通过object调用类成员函数时,有一个指向这个object的指针被传递给这个函数,这个指针叫做this指针。
例如当我们调用:
total.price();
实际上相当于:
price(&total);//pseudo-code
因为this永远指向当前object,因此this是一个const指针,我们不能改变this的地址。但是this指向的object当然是可以改变的。因此this是一个top-level const pointer,不一定是low-level const pointer。
1.2 const 成员函数介绍
类成员函数有时被声明为const类型,这意味着该类成员函数不会修改this指向的object。而没有被声明为const类型的类成员函数,则自然可以修改这个object。因此,如果object是const,那么我们就不能调用nonconst类成员函数。
class testConst
{
public:
int constMemberFunction(){ return 0; }
int constMemberFunction2() const { return 0; }//const 类成员函数
};
testConst a;
a.constMemberFunction();//ok
const testConst b;
b.constMemberFunction();//error!
b.constMemberFunction2();//ok!
具体说来,在上面的例子中,第一个类成员函数constMemberFunction将this指针定义为int* const类型,而在第二个类成员函数constMemberFunction2中将this指针定义为const int * const类型。大家可以自行体会。
1.3 类内顺序(class scope)
在类的定义中,先出现的函数可以在函数体内调用后出现的变量。即:
class scopeTest
{
int print(){cout << answer << endl;}//ok!尽管answer在后面定义
int answer;
};
这是因为,C++编译器在编译类时分两步,第一步:对所有变量和类成员函数的声明进行编译;第二步:处理函数体。
Return “this” object
直接返回this指向的object,一般的写法是:
class thisTest
{...};
thisTest& thisTest::add(const thisTest&)
{
...
return *this;
}
1.4 构造函数(Constructors)
和其他函数不同的是,构造函数没有返回类型。一个类可以有多个构造函数,只要满足函数重载的规则即可。
构造函数不能定义为const函数,这很容易理解。即使我们要创建一个const object,在创建的过程中我们无疑需要对这个object进行一些赋值。
默认构造函数(default constructor)
默认构造函数没有任何参数。默认构造函数可以显式声明,只要没有参数就属于默认构造函数。
编译器自动生成的构造函数通常被称作:synthesized default constructor。在这个函数中,编译器遵循如下法则初始化类内各个成员:
1.如果有in-class initializer(在类内直接赋初值),则使用这个值。
2.否则,对成员进行default initialize。(int等被赋为0等)
有时不能依赖synthesized default constructor
1.只有当类没有定义任何构造函数时,编译器才会为我们定义一个synthesized default constructor
2.当成员是built-in type 或 compound type时(arrays , pointers等),default initialize会产生undefined value,这是我们绝对不希望的。
3.有时,编译器无法完成synthesized default constructor的工作。比如,一个类内部有一个成员是另一个类,而那个类没有构造函数,等等情况。
=default
有时,我们在定义各个类内成员时,已经写好了in-class initializer,那么可能会觉得再写一个构造函数比较麻烦,这时可以用=default 语句。
class defaultConstructorClass
{
...
defaultConstructorClass() = default;
};
一般来说,只有当我们同时需要定义其他的构造函数时,才会这么写,否则,直接不定义不就好了嘛。
constructor initializer list
在定义构造函数时,有一个比较与众不同的东西叫做constructor initializer list。
形式如下:
className(string &s) : bookNo(s){}
className(string &s,unsigned n,double p) : bookNo(s),units_sold(n),revenue(p*n) {}
及时我们需要将构造函数定义在类外,这种constructor initializer list也依然是可以使用的。
一般来说,最好能使用in-class initializer,但如果编译器不支持,那我们必须这样声明。
2. 控制Class以及包裹Class
2.1 class和struct关键词
struct和class几乎是可以互换的,只是一种风格的不同。
唯一的区别是,默认情况下(没有声明public,private等),struct下的成员是public的,而class下的成员是private的。
至于编程风格上,如果我们的类中所有成员都是public的,那么我们用struct。如果有private成员,那我们用class。
2.2 友元(Friends)
将其他类或函数声明为友元(friends)后,该类或函数就能调用本类中的非public成员。
例如:
class friendClass
{
friend friendClass add(...);//一个类外函数
friend anotherClass;//另一个类
};
friends函数不受private或public影响,但是作为好的习惯,我们应该将他们放在一起,放在class中的最开始,或者最后。
另外,class内的友元函数的声明,只是表明一个权限(access),而不是一般意义上的声明。如果在类的外部我们需要使用这个友元函数,那么我们还需要再次声明这个函数!
一般来说,我们会将这些友元函数的声明和这个类放在同一个头文件中。
而如果是将一个类声明为友元,则可以在两个头文件中都包含一下另一个类的简单声明。(这个是我自己认为的)
3.类的其他特性
3.1 类成员
Type Member
在类的定义中,我们还可以定义type。type也会受到public和private的限制。
class testConst
{
public:
using publicint = int;
private:
using privateint = int;
};
testConst::publicint a = 0;//ok!
testConst::privateint b = 0;//error!
值得注意的是,和其他类成员不同,type member必须先定义,再使用!因此,一般讲typedef / type alias声明在类的最前面。
Inline Members
前面提到过,在类的内部完全定义好的函数默认是inline的。同时我们也可以显式要求某个函数是inline的。
我们可以在类的内部声明inline标示符,可以在类外部定义时声明inline标示符,也可以在两个地方都声明,效果是一样的。但是,C++ Primer建议,只在类的外部声明in