赞
踩
建议先看完上面那篇博客。这篇博客主要讲述C++中类和对象的定义与使用。
先介绍一些概念,建立理论印象。
所谓对象,其实就是用来描述客观事物的一个实体,比如一辆汽车就是一个对象。描述一个对象时,一般会从它的属性和行为两方面来展开,其中,属性可以认为是对象的静态特征,一些数据;而行为可以认为是对象的动态特征,对应的操作等。
而类,是具有相同属性和服务的一组对象的集合,它是属于该类的全部对象的一个抽象描述,因此同样具有属性和行为两部分,但这里更多的是抽象的概念。
类和对象的关系犹如模具和铸件之间的关系,一个属于某类的对象称为该类的一个实例。用类定义一个对象的过程也叫实例化。
对象的类型就是类。
这里说的封装可不是电路板中元器件的封装,而是指代码的封装。
具体来说就是把对象的属性和服务结合成一个独立的系统单元,比如一个类。同时尽可能隐蔽对象的内部细节,对外形成一个边界,只保留有限的对外接口使之与外部发生联系。像这种只暴露公有接口,而隐藏私有实现的数据称为抽象数据类型。
在类中,常用public关键字来显现类的接口;而用private来隐藏类的实现。通常数据成员是隐藏的,并对不隐藏的成员函数提供支持。
所谓继承,是指特殊类(子类)拥有其一般类(父类)的全部属性和服务,称作特殊类对一般类的继承。比如交通工具是父类,那么地铁就是其中一个子类。当然这个继承可以有多层。
多态是指在一般类(父类)中定义的属性或行为,被特殊类(子类)继承之后,可以具有不同的数据类型或表现出不同的行为。这使得同一个属性或行为在一般类及其各个特殊类中具有不同的语义。
这个概念初看可能会有点懵,举个例子:动物(父类)中有一个成员函数是发出叫声,猫(子类)中也有一个成员函数也是发出叫声,但它们的返回值不同(一个喵喵喵,一个不确定),那么在调用这个函数时,如果选定的作用域不同,那么得到的返回值也不同。
从前面讲述的类和对象的关系可以看出,在代码里面,我们都要先声明一个类,然后用这个类来定义对象。值得注意的是:类是抽象的,不占用内存,而对象是具体的,要占用内存。
看一个声明类的示例:
class Student //声明类类名
{
private: //声明私有成员
int num;
char name[20];
char sex;
public: //声明公有成员
void display()
{
cout<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
}
};
从这个示例中,我们可以看出:
声明完类之后,接下来就是使用类来定义对象了,一般来说,定义对象有三种方式:
class Student stu1, stu2; //这种是从C语言中继承下来的
Student stu3, stu4; //C++独有的方式而且使用也更广泛
class CDate
{
int year;
int month;
int day;
public:
void SetDate(int, int, int);
void ShowDate();
}myBirthday; //同时创建对象myBirthday
class //class开头 无类名
{
private: //声明私有部分
…
…
public: //声明公用部分
…
…
}stu1,stu2;//定义了两个Student类的对象stu1与stu2
这种方式也是合法的,但是不建议使用。 参考链接及代码来源
此外,也可以使用类来定义对象数组和对象指针。需要注意指针访问成员函数和成员变量时应该用->
符号,且在使用时一定要初始化。
Student *stu1, st[10];
stu1 = &st[0]; //指针使用前一定要初始化,即指向一个实际的对象。
//调用时
stu1->num = 110;
//或者这样
(*stu1).num = 110;
st[1].num = 111;
在前面,我们学习了new
和delete
两个运算符来动态分配和删除变量的内存,其实它们不仅可以用在变量,还可以用在对象上。
使用示例:
class Box
{
....
};
int main()
{
Box *pt, box1;
pt = new Box;
pt = &box1; //定义一个指针指向box1这个对象。
....
delete pt; //释放内存
}
前面提到,类中的成员函数也可以在类内直接定义,但大部分的程序不会这样干,因为它会自动变成内置函数,不适合较大的程序。一般来说,都是先声明类,同时声明类中的成员函数,然后再类外对函数进行定义。类外函数定义的基本结构:
this
来指代本类。举个例子class Box { int length; int width; int height; public: void getvolume(); } void Box::getvolume() { cout << length*width*height << endl; } //也可以写成下面这样 void Box::getvolume() { cout << (this->length)*(this->width)*(this->height) << endl; }
所以,this
更多地可以看成是类的别名。
如果一个类仅被一个程序调用,可将类的声明和成员的定义放在文件开头。但如果类被多个程序调用,则可以将类的声明(包括成员函数的声明)放在一个头文件中(.h),用户使用类时,只需要include该头文件即可。这就是类的封装。而这也是形成类库的方式。
// student.h(头文件) class Student { ... } // Student.cpp (实现文件,必须与头文件同名) void Student::func() { ... } .... // main.cpp (主程序) #include "Student.h" //包含头文件,一般自定义的头文件要使用双引号。 int main() { ... }
此外,如果为了隐藏源码实现,但又能够让用户使用该库中的类,就可以将.cpp和.h文件单独编译成一个.obj文件,然后由用户添加到工程项目中,这样也能实现内部类的调用。
在声明类时,往往会将一些数据成员设为private,但这样也就意味着外界无法访问这些变量,也就无法赋值,因此往往还需要添加一个设置数据的函数,有点不太方便,而构造函数就是定义类时自带的一个可以用来初始化类内数据变量的一个函数。【类内的成员变量不能在声明时直接初始化一个值,除非用const static
修饰 参考链接】
class student { private: int num; int sex; public: student() //自定义一个无参数的构造函数 { num = 10; sex = 5; } void getnum(); }; void student::getnum() { cout<<num*sex<<endl; } int main() { student stu1; //定义变量时就已经调用自定义构造函数进行初始化了 stu1.getnum(); system("pause"); return 0; }
class student { private: int num; int sex; public: student(int num1) { num = num1; sex = num * 2; } student(int num1, int sex1) { num = num1; sex = sex1; } void getnum(); }; void student::getnum() { cout<<num*sex<<endl; } int main() { //student stu1; //err: 没有合适的构造函数 student stu2(10); //调用第一个构造函数 student stu3(10, 12); //调用第二个构造函数 stu2.getnum(); stu3.getnum(); system("pause"); return 0; }
带缺省参数
构造函数也是支持缺省参数函数的,也就是对一些参数指定默认值,但需要注意的是,带缺省参数的函数如果重载的话,一定不能冲突,这个在上一篇教程中已经强调过。
需要注意:如果定义了缺省参数的构造函数,相当于对构造函数进行重载;而且指定默认值要在声明时指定。
5.3 参数初始化列表
参数初始化列表这个东西个人觉得非常复杂,其实说白了它其实就是一种比较特殊的变量赋值方式,在一些情况下和普通的变量赋值可以替换,但在一些场景下只能使用这种方式对一些变量进行初始化。
具体可以参考这篇博客。值得一提的是它提到的派生类在定义构造函数时,要考虑父类初始化的问题。这是C++设定的机制。
附:类成员变量初始化总结
值得一提的是,所谓生存期结束,是指执行return 0;
这行代码时才会显示,所以调试的时候需要注意。另外,如果一个程序有多个对象,那么一般是先构造的先析构。
#include <iostream> #include <string> using namespace std; class Student { public: Student(int n,string nam,char s) { num=n; name=nam; sex=s; cout<<"Construtor called"<<endl; } ~Student() { cout<<"Destructorcalled"<<num<<endl; } void display() { cout<<"num:"<<num<<endl; cout<<"name:"<<name<<endl; cout<<"sex:"<<sex<<endl; } private: int num; string name; char sex; }; int main() { Student stud1(10010,"wng",'f'); stud1.display(); Student stud2(10011,"zng",'m'); stud2.display(); return 0; }
这个程序在Devc++中执行得到的结构如下图所示:
注意:析构函数不仅仅是在对象生命期结束才会调用,还有其他的一些情况,参考这个链接。如果想加深对这方面的认识,可以看看这篇文章里面的题目。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。