1.类、成员的声明,定义,初始化的基本规则
C++中类的基本模板如下:
namespace 空间命名{//可以定义namespace,也可以不定义 class/struct 类名称{
public/private: 基本成员; 构造函数():成员初始化列表{ 构造函数主体; }
返回类型 函数名称(参数列表) 修饰符{
函数主体;
} }; }
例如:
//Person.h #pragma once #include <string> using namespace std; class Person{ private://定义私有成员 string name; int age; public://定义公共成员 Person() = default;//c++11标准中使用=default,定义构造函数的默认行为 Person(string nm,int ag) : name(nm),age(ag){}//带两个参数的构造函数 Person(string nm) : name(nm){}//带一个参数的构造函数 Person(int ag) : age(ag){} /* * 或者写成: * Person(string nm) : Person(nm,0){}//调用带两个参数的构造函数,初始化age为0 * Person(int ag) : Person("",ag){}//调用有两个参数的构造函数,初始化name为"" * */ void setName(string name){ this->name = name; } string getName(){ return this->name; } void setAge(int age){ this->age = age; } int getAge(){ return this->age; } }; //PersonTest.cpp #include "Person.h" #include <iostream> using namespace std; int main(int argc, char * argv[]){ Person person("jamy",20), *p = &person; //也可以写成:Person person = Person("jamy",20); cout << "name:" << p->getName() << "\n" << "age:" << p->getAge() << endl; return 0; }
在定义类的时候,可以使用class关键字或struct关键字。这种变化仅仅是形式上有所不同,实际上我们可以使用这两个关键字中的任何一个定义类。唯一的区别是struct和class的默认访问权限不太一样。如果我们使用struct关键字,则定义在第一个访问说明符之前的成员是public的;相反,如果我们使用class关键字,则这些成员是private的。
出于统一编程的考虑,当我们希望定义的类的所有成员是public的时,使用struct;反之,如果希望成员是private的,使用class。
构造函数的名字和类名相同。和其他函数不一样的是,构造函数没有返回类型;构造函数也有一个(可能为空的)参数列表和一个(可能为空的)函数体。构造函数的初始值是成员名字的一个列表,每个名字后面紧跟括起来的成员初始值,不同成员的初始值通过逗号分隔开来。例如上面的:
Person(string nm,int ag) : name(nm),age(ag){}
其中name(nm),age(ag){},表示初始name成员为nm值,初始age成员为ag值,函数体为空。在函数体中,也可以改变参数的值:
Person(string nm,int ag){ this->name = nm;//赋值,并非初始化 this->age = ag;//赋值,并非初始化 }
但上面这段代码并没有初始化name和age值,他们只是重新修改name和age的值。并且有些特殊成员(例如引用和constexpr)是不运行这种方式的,所以建议使用初始化的方式。
2.this使用的注意点
需要注意在C++中,this不是代表本类对象,而是指向本类对象的指针。在使用的需要注意,this是一个指针。
成员函数通过一个名为this的额外隐式参数来访问调用它的那个对象,当我们调用一个成员函数时,用请求该函数的对象地址初始化this。
例如:
person.getName();
则编译器负责把person的地址传递给person的隐式形参this,可以隐式的认为编译器将该调用重写成了如下的形式:
Person::getName(&person);
其中调用Person的getName成员传入了person对象的地址。
默认情况下,this的类型是指向类类型非常量版本的的常量指针。在Person的类型中,this的默认类型是Person *const。
this的默认类型是Person *const,那么有没有其它的类型呢?答案是肯定的,当我们在定义函数的时候指定const关键字,那么this就是指向类类型常量版本的常量指针,在Person类中也就是 const Person * const类型。
例如:
void getName() const{//只能取值,不能修改调用对象的属性值 return this->name;//this的类型是const Person * const }
3.静态成员
和其他成员函数一样,我们既可以在类的内部也可以在类的外部定义静态成员函数。当在类的外部定义静态成员函数时,不能重复static关键字,该关键字只出现在类内部的声明语句。
因为静态数据成员不属于类的任何一个对象,所在它们并不是在创建类的对象时被定义的,这意味着它们不是由类的构造函数初始化的。而且一般来说,我们不能在类的内部初始化静态成员。相反的,必须在类的外部定义和初始化每个静态成员。但如果给静态成员加上constexpr,那么就可以在类内初始化了。
在类外的定义中,定义静态数据成员的方式和定义成员函数差不多,需要指定对象的类型名,类名,作用域运算符以及成员函数的名字,
在类外初始化静态成员的格式如下:
数据类型 类名::静态成员名 = 初始值
//Account.h class Account{ public: static double rate(){return interestRate;} static void rate(double); private: static double interestRate;//声明,不能有初始值,不能在类内部初始化 static 成员 static constexpr int period = 30;//可以在类内部初始化 static constexpr 成员 }; //Account.cpp #include "Account.h" double Account::interestRate = rate();//初始化静态成员interestRate,不能再次使用static关键字。这里等同于double Account::interestRate = interestRate。经过interestRate的赋值后,interestRate的值就是0. void Account::rate(double newRate){//初始化静态函数rate,不能再次使用static关键字 interestRate = newRate; }
上面案例中,Account::interestRate = rate()语句相当于Account::interestRate = interestRate,当静态变量定义后就会分配内存空间。这里interestRate是int类型,所以默认值为0。
下面的案例有比较清晰的解释:
#include <stdio.h> class A { public: static int a; //声明但未定义 }; int main() { printf("%d", A::a); return 0; }
编译以上代码会出现“对‘A::a’未定义的引用”错误。这是因为静态成员变量a未定义,也就是还没有分配内存,显然是不可以访问的。
#include <stdio.h> class A { public: static int a; //声明但未定义 }; int A::a = 3; //定义了静态成员变量,同时初始化。也可以写"int A:a;",即不给初值,同样可以通过编译 int main() { printf("%d", A::a); return 0; }
这样就对了,因为给a分配了内存,所以可以访问静态成员变量a了。
注意:在类外面 这样子写 :int A::a ; 也可以表示已经定义了。