目录
- 三、类和对象
1、类的概念
2、类和对象的关系
3、编写规范
4、对象的引用(使用)
5、类的作用域
6、构造函数和析构函数
7、构造函数
8、析构函数
- 三、类和对象
1、类的概念
类是具有共同性质的一类事物的统称, 而对象是根据抽象的概念实例化出来的一个实际对象
C 结构: 结构体成员是数据; C++中的类: 类的成员是数据和函数(数据成员、成员函数)
成员的访问权限有:
1) 私有成员: private, 它只能被该结构中的其他成员访问
2) 保护成员: protected 不涉及到派生的时候与 private 一样, 后面会详细讲解
3) 公有成员: public, 即可以被结构中的其他成员访问, 又可以被结构外的其他成员访问
把结构体的struct换成class即成为类的定义
类的数据成员默认访问权限是私有的,类的成员和结构体成员的访问方式一致,都是用点
#include<iostream>
using namespace std;
class stu{
public :
int num;
void setAge(int a)
{
age = a;//即使age在下面定义也可以在上面先使用,C++认为类是一个整体
}
int getAge(void)
{
return age;
}
private:
int age;
char name[100];
};
int main()
{
stu lucy;
lucy.num=100;
cout << "lucy.num = "<< lucy.num<<endl;
lucy.setAge(18);
cout << "lucy.getAge()= "<<lucy.getAge()<<endl;
return 0;
}
类的声明中的 private 和 public 两个关键字可以按任意顺序出现任意次。 为了使程序更加清晰, 把所有私有成员
和公有成员归类放在一起
数据成员可以是任何数据类型, 但不能用 auto、 register、 或 extern 说明
不能在类的声明中给数据成员进行初始化
2、类和对象的关系
类---自定义的数据类型;对象(实例) ---变量
类的大小只是其所有成员的大小之和(与对齐方式也有关),但是和类中的方法无关
定义对象之后, 会为对象分配存储空间
class STU{
private://不加 private 或 public 或 protected 默认是私有数据
int num;//私有数据在类中可以识别
char name[32];
//为什么把数据声明为私有的呢, 就是为了封装, 只能在类内使用, 不能在类外访问
//可以用公有的方法访问或设置私有数据
public://公有数据在类外也可以识别
void setNum(int x)//方法
{
num=x;
}
int getNum()
{
return num;
}
void setName(char *str)
{
strcpy(name,str);
}
char *getName(void)
{
return name;
}
};
对类的设计的时候, 对于任何一个成员数据, 都要有 set 和 get
对多个对象来说, 数据是独有的, 方法是共有的, 即每个对象的数据都是其独有的, 而类的方法是所有对象共有的
数据成员一般都是设置成私有的, 如果设置成公有的, 就会破坏 C++的封装性
3、编写规范
一般在类内进行方法声明,类外定义
类的定义一般放在头文件中
stu.h
class STU{
private://不加 private 或 public 或 protected 默认是私有数据
int num;//私有数据在类中可以识别
char name[32];
//为什么把数据声明为私有的呢, 就是为了封装, 只能在类内使用, 不能在类外访问
//可以用公有的方法访问或设置私有数据
public://公有数据在类外也可以识别
void setNum(int x)//方法;
int getNum(void);
void setName(char *str);
char *getName(void);
};
stu.cpp
#include <cstring>
#include "stu.h"
void STU::setNum(int x)//方法
{
num=x;
}
int STU::getNum(void)
{
return num;
}
void STU::setName(char *str)
{
strcpy(name,str);
}
char *STU::getName(void)
{
return name;
}
4、对象的引用(使用)
使用方法:对象名.数据成员名
对象名.成员函数名(实参表),也可通过指针运算符来引用(需提前定义同类型的类指针变量)
lucy.setNum(200);实际上是 lucy.STU::setNum(200); 的缩写, 两者等价
外部函数不能引用对象的私有成员: 如 lucy.num 是错误的。 即私有成员不能在类外引用
同一类的对象可以相互赋值
如: STU lucy,bob;lucy.setNum(100);bob=lucy;
此时是将所有数据成员逐个进行拷贝(浅拷贝)。 则 cout<<bob.getNum()<<endl; //结果为 100
类似于相同类型的结构体变量是可以相互赋值的。
浅拷贝:对于普通的整形或数组变量无影响,对于指针变量有可能出错
两个类对象的指针成员指向同一块内存;释放的时候有可能被释放两次
如果在拷贝的时候开辟出另一块内存,指针指向新内存则为深拷贝
5、类的作用域
定义类的时候一对花括号所形成的作用域,最外层的花括号
一个类的所有成员都在该类的作用域内
C++把类的所有成员都作为一个整体的相关部分,无定义的向后顺序之分
一个类的任何成员可以不受限制的引用该类的其他成员;只有公有成员才可以被外部函数引用,体现类的封装功能
6、构造函数和析构函数
当定义一个对象的时候, 编译程序需要为对象分配存储空间, 进行必要的初始化, 初始化的工作随着类的不
同而不同。 在 C++中, 可以由构造函数来完成这初始化工作。
构造函数是系统自动调用的, 不是在程序中人为调用。 在程序中实例化一个对象的时候, 自动调用。
与构造函数相对应的是析构函数, 当撤销类对象的时候, 析构函数就回收存储空间, 并做一些善后工作。 析
构函数也是系统自动调用的。 不需要在程序中显式的调用。
构造函数和析构函数都属于某一个类, 它们可以由用户提供, 也可以由系统自动生成。
系统自动生成的构造函数和析构函数, 函数体都是空的。
7、构造函数
构造函数是一种特殊的成员函数, 它主要用于初始化对象,属于类内
每一个类都必须有一个构造函数。 如果没有给类定义构造函数, 则编译系统自动生成一个空的构造函数(参
数和函数体均为空);如果用户自定义了构造函数, 那么 C++就不用提供默认构造函数
构造函数具有一些特殊的性质
a、构造函数的名字必须与类名相同
b、定义对象时被系统自动调用
c、可以有任意类型的参数(也可以不带参数) , 但不能有返回值, void 也不可以
d、被定义为公有的, 但其他时间无法被调用
e、一旦用户实现了构造函数, 系统默认的构造函数就会被屏蔽
#include<iostream>
#include<cstring>
using namespace std;
class STU{
private:
int num;
char name[32];
public:
STU(void);
STU(int n,const char *str);//STU 类的构造函数
void setNum(int x);
void setName(char *str);
int getNum(void);
char *gevoid display(void);
};
void STU::setNum(int x)
{
num=x;
}
void STU::setName(char *str)
{
strcpy(name,str);
}
int STU::getNum(void)
{
return num;
}
char *STU::getName(void)
{
return name;
}
STU::STU(void)
{
cout<<"无参的构造函数"<<endl;
}
STU::STU(int n,const char *str)//STU 类的构造函数
{
num=n;
strcpy(name,str);
cout<<"in 构造函数 "<<name<<endl;
}
void STU::display(void)
{
cout<<"num= "<<num<<endl;
cout<<"name= "<<name<<endl;
}
int main()
{
STU lucy(101,"lucy");//调用有参的构造函数
cout<<"--------------------"<<endl;
STU bob;//调用无参的构造函数
cout<<"--------------------"<<endl;
STU *p;
p = new STU(100, "TOM");//调用有参构造函数
return 0;
}
构造函数是可以重载的;如果不想写无参的构造函数, 那就把带参的构造函数写成带缺省参数的就可以了
构造函数也可以带缺省参数,注意重载和缺省参数的 二义性
8、析构函数
析构函数也是一种特殊的成员函数。 它执行与构造函数相反的操作, 通常用于执行一些清理任务, 如释放分
配给对象的内存空间等。
主要处理指针变量。如果类中数据成员没有指针变量, 析构函数基本上没有用处。
析构函数与构造函数名字相同, 但它前面必须加一个波浪号(~)
析构函数没有参数, 也没有返回值, 而且不能重载, 因此一个类中只能有一个析构函数
当撤销对象时, 编译系统会自动的调用析构函数
#include<iostream>
#include<cstring>
using namespace std;
class STU{
private:
char *name;
public:
STU(int n,const char *str);//STU 类的构造函数
void setName(const char *str);
char *getName(void);
~STU(void);//STU 类的析构函数
};
void STU::setName(const char *str)
{
delete []name;
name=new char[(strlen(str)+1)];
strcpy(name,str);
}
char *STU::getName(void)
{
return name;
}
STU::STU(int n,const char *str)//STU 类的构造函数
{
num=n;
name=new char[(strlen(str)+1)];
strcpy(name,str);
cout<<"in 构造函数 "<<name<<endl;
}
STU::~STU(void)
{
cout<<"析构函数"<<endl;
delete []name;
}
int main()
{
STU lucy(101,"lucy");//调用有参的构造函数
cout<<"--------------------"<<endl;
cout<<"程序即将结束"<<endl;
return 0;
}
程序结束释放对象时调用析构函数
成员中要是没有指针就可以不定义析构函数,使用系统默认的即可
大括号结束就执行lucy的析构函数
int main()
{
{
STU lucy(101,(char *)"lucy");//调用有参的构造函数
cout<<"--------------------"<<endl;
}
cout<<"程序即将结束"<<endl;
return 0;
}
在delete一个对象的时候执行析构函数
int main()
{
cout<<"---------0000---------"<<endl;
STU *p=new STU(100,"lucy");
cout<<"---------0001---------"<<endl;
delete p;
cout<<"---------0002---------"<<endl;
return 0;
}
在同一作用域内类对象的构造和析构的执行顺序: 先构造的后析构