1. 结构和类的区别
一个结构如下:
可以说结构只是类的一部分,struct声明的结构体类型实际上也是类类型。区别是,用struct声明的结构体类型,默认是public的,而用class定义的类,默认是private的。
2. 成员函数在类之外定义
void Student::display()
{…}
3. inline声明
在类内定义的简单成员函数会被系统默认声明为inline,在类外定义的函数,如果想将指定为内置函数,应当与inline作显式声明,如:
4. 对象成员的引用
5.综合例子
6.构造函数
用于类对象的初始化,有下面几种形式:
形式一:在内部定义并初始化(无参数形式)
形式二:在内部声明,在外部定义并初始化(无参数形式)
注:“无参数的构造函数”的特点是:一次性的初始化,以后不能再调用构造函数重新初始化。
形式三:在内部声明,在外部定义,不初始化
形式四:在外部采用“初始化表”的形式来定义,不初始化
形式五:形式一和二的带参数和默认值的形式
类似于形式三和四,只需要在声明时写作:
Student(int=10102 ,string=”Wang xiao”, char=’f’);
即可。注:和“无参数的构造函数”相比,有参数的构造函数可以多次调用构造函数来进行多次初始化,而无参数形式只能一次性初始化。
形式六:不同个数参数的构造函数(既构造函数的重载)
可以说是形式一到五的一般形式:
7.多文件类和构造函数的例子
一个三个文件student.h, student.cpp和main.cpp,内容如下:
多文件编译时要把3个文件都放入同一个工程文件中(其中.h文件会自动包含进来,但是.cpp文件需手动加入),具体方法:先打开main.cpp文件然后编译它(Compile),先不忙建立连接(Build),打开student.cpp文件编译,然后再连接main.cpp文件,最后执行main.cpp就OK了。该构造函数采用了3个参数的形式。
8.相关延伸
a. 对象的复制,拷贝(复制)构造函数
例如:Box box2(box1); //利用box1克隆出一个新对象box2
实际上这个函数的形式是:
C++还提供了另一种复制形式:
Box box2=box1;
系统会根据实参的类型决定调用普通构造函数还是复制构造函数。例如:
例2:
b. 析构函数
当对象的生命周期结束后会自动调用析构函数。析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作。它还可以被用来执行“用户希望在最后一次使用对象之后所执行的任何操作”,例如输出有关信息。例如:
c. 类对象数组
定义方式:
d. 对象指针
1.指向对象的指针:
2.指向数据成员的指针:
3.指向成员函数的指针:
4.this指针
this指针的作用:如果对同一个类定义了n个对象,这有n组同样大小的空间存放n个对象中得数据成员,而不同的对象所调用的都是同一个函数代码段,当不同对象的成员函数引用数据成员时,如何能保证引用的是所指定的对象的数据成员呢,这就是this指针的作用。
例如调用t1.volume();实际上系统隐含转换成t1.volume(&t1);虽然volume并没有参数,但是为了识别出t1的数据成员,会把t1当做参数传递,内部用this指针指向t1的具体参数。即把函数定义处理为:
这样调用t1.volume()时,即调用t1.volume(&t1),把&t1赋值给this指针,即this是指向对象的指针。
e. 常对象,对象的常引用
1.常对象:
2.常数据成员:
const int hour;
这样定义后hour就是常数据成员了,它不能被赋值,如果要初始化,应该用初始化表的形式,即:
Time::Time(int h):hour(h){}
3.常成员函数
void get_time() const; //把const放在最后
4.指向对象的常指针
也可以分两行写:
Time * const p1;
p1=&t1;
5.指向常对象的指针
例如:
指向常对象的指针最常用于函数的形参,目的是保护所指的对象不被修改,例如:
6.对象的常引用
一个变量的引用其实就是变量的别名。例:
f. 对象的动态建立和释放
例如:
Box *pt;
pt=new Box;
在程序中就可以通过pt访问这个新建的对象了:
cout<<pt->height;
C++还允许在执行new时,对新建立的对象进行初始化:
Box *pt= new Box(12,15,18); //推荐形式
用delete运算符予以释放:
delete pt; //在释放内存空间之前,自动调用析构函数完成有关善后清理工作
其实,之所以称之为“动态”,是因为这样开辟的内存空间是没有名称的,如果不用new运算符,必须写作:
Box t(12,15,18);
Box *pt=&t;
此时的内存空间是对象t,而用new时就没有名字。
一,静态成员
1. 静态数据成员
类的对象数据成员都是各自调用各自的,不能为所有对象所共享。使用全局变量又有到处都可以修改全局变量的值的弊端(静态数据成员的值也是可以被修改的!之所以称为“静态”,是因为修改后不复原)。因此实际工作中很少使用全局变量。如果想在同类的多个对象之间实现数据共享,也不要使用全局变量,可以使用静态的数据成员。例如:
静态数据成员不只属于某个对象,所有对象都可以引用它,即使不定义对象,也为静态数据成员分配空间,它也可以被引用!(它可以通过对象名引用,也可以直接通过类名来引用),例如:
静态数据成员可以初始化,但只能在类外。如:
int Box::height=10; //注意,初始化时前面要加类型,比如int
不能用参数初始化表对它进行初始化。如:
Box(int h, int w, int len):height(h){} //是错误的
如果未对静态数据成员赋初值,则编译系统会自动赋初值0。
2. 静态成员函数
和静态数据成员的定义和调用类似:
static int volume();
要注意的是:静态成员函数没有this指针,由此决定了静态成员函数不能直接访问本类中的非静态数据成员(可以用传参数的形式访问),只能直接引用本类中的静态数据成员。例如:
如要使静态函数static float average()访问非静态数据成员,例如访问stud[0].score,这应该用函数传参数的形式,例如:
声明:
static float average(const Student &);
定义:
float Student::average(const Student &stu)
{
cout<<stu.score<<endl;
return(sum/count);
}
引用:
cout<<Student::average(stud[2]);
二、友元
friend函数可以是普通非成员函数或者另一个类中的成员函数。
1.普通函数声明为友元函数
直接在public中加入一句声明:
friend void display(Time &);
在类外定义的友元函数display()内容如下:
void display(Time &t)
{cout<<t.hour<<”: ”<<t.minute<<endl;}
注意,display是一个在类外定义的且未用类Time作限定的函数,它是非成员函数,不属于任何类。用friend声明后就可以应用Time类中的私有成员。
2.另一个类的成员函数声明为友元函数
3.友元类
好比一个家庭不仅允许一个好朋友可以进入他们的家庭,还允许他全家的人都可以进入他们的卧室。声明友元类的方式:
friend 类名;
在实际工作中,除非必要,一般不把整个类声明为友元类。
三、类模板
函数模板是针对于功能相同而数据类型不同的一些函数。同理,类模板也是如此。
声明类模板:
template <class 类型参数名>