类
- 类是C++的灵魂,C++中对象的类型称为类(class)。
- 一个类可以定义多个对象,每个对象包含类中定义的各个属性的存储空间.
类的声明
声明一个类类型与声明一个结构体类型相似;
class 也可以写为 struct ; 区别:如果不带成员访问限定符,class类的所有成员默认限定为private;struct类的所有成员默认限定为public.
声明结构体
#include <iostream>
using namespace std;
// 定义结构体类型
struct student {
int num; // 学号
char name[20]; // 姓名
char sex; // 性别
};
student st1,st2; // 定义结构体变量
void setdata(student *p) { // 设置
cin >> p->num;
cin >> p->name;
cin >> p->sex;
}
void display(student s) { // 显示
cout << s.num << endl;
cout << s.name << endl;
cout << s.sex << endl;
}
int main()
{
setdata(&st1);
setdata(&st2);
display(st1);
display(st2);
声明类和具体使用
- 在定义类时,三类成员不分前后顺序,也可以重复出现。
- 如果成员函数的成员访问限定符是public,则对象可以通过它访问类的其 他 成 员 ; 如 果 是 p r i v a t e 或protected,则对象不能用来访问类的其他成员。
class student{
private :
int num;
char name[20];
char sex;
public:
void inpdata(){
cin>>num;
}
void displaydata(){
cout<<num<<endl;
}
};
int main(){
student no1;
no1.inpdata();
no1.displaydata();
}
类外声明函数
类外定义成员函数时,必须在函数中增加类名,用于限定它属于哪个类,并且在声明最前面加上函数类型
::是作用域限定符或作用域运算符.(范围为全局)。
class student
{ int num;
string name;
char sex;
public:
void setdata();//类内限定
};
void student :: setdata()//类外定义
{ cin >> num;
cin >> name;
cin >> sex;
}
类的存储
只为对象的数据成员分配内存空间,一个类的所有对象共享一个成员函数空间。
对象的成员的访问
成员如果为数据则必须有值才能访问。
用成员运算符访问成员
st1.display(); // 调用成员函数
用指针访问成员
可以指到对象也可以指到成员。
int main()
{
Time t,*p;
p= &t;
cout<< p->hour<<endl;
return 0;
p->hour表示p当前指向对象t中的成员hour,
(*p).hour t.hour 成员运算符 和 p->hour 指针都可以表示对象t中的hour
用引用访问成员
Time t1;
Time & t2=t1;//引用t2 访问t1
cout<< t2.hour<<endl;
声明与函数定义分离
对两个.cpp文件分别编译得到.obj文件生成.exe文件
类库
在实际中,将若干个常用的功能相近的类声明集中在一起,形成类库。类库包括C++编译系统提供的标准类库,用户的类库。类库有两个组成部分:(1)类声明头文件;(2)经过编译的成员函数的定义,它是目标文件
对象的初始化
对公有对象进行初始化。
class Time
{ public:
int hour;
int minute;
int sec;
};
Time t={ 14, 56, 30 }; // 定义对象并初始化成员
构造函数
- 构造函数的名字必须与它的类名相同,因此不需要显式调用,建立对象时系统自己会调用。
- 没有返回类型,也没有void
- 可以不带参数或带任意类型的参数 主要完成数据成员的初始化工作
如果没有定义构造函数,系统会自动生成 一个没有参数没有代码的默认构造函数
一般有三种构造函数:默认构造,复制(拷贝)构造,带参构造
Time(){} // 默认构造函数
Time() // 复制构造函数
{ hour=0; minute=0; sec=0; }
Box::Box(int h, int w, int len){ //带参构造函数的类外声明
//类内声明,main调用都为Box(int, int, int);
height = h;
width = w;
length = len;
}
默认参数的构造函数也可以写为 Box::Box( int h, int w, int len) : height ( h), width(w), length( len ) }
构造函数一般定义为public。
在构造函数中除了可以对数据成员赋初值,还可以写其他语句。
如果某函数的返回值是一个对象,则该函数被调用时,返回的对象是通过复制构造函数初始化的
带形参的构造函数
带形参的构造函数在定义对象时必须指定实参
class student
{public:
student(int n;char s,name[]):num(n),sex(s),{strcpy(Name,name);}
//把形参n 得到的值赋给私有成员num;把数组name的值用复制函数strcpy 赋值到私有数组Name;最终三个都初始化;
private;
int num;
char sex;
char Name[20];
};
重载构造函数
重载时,可能会与默认参数函数冲撞。
比如:
//构造函数:
Box();
Box(int ,int =10,int =10);
Box(int, int );
// 定义对象
Box box1; // 正确,调用第一个
Box box2(15); // 调用第二个
Box box3(15, 30 ); //不知调用第二个还是第三个
析构函数
析构函数没有数据类型、返回值、形参。由于没有形参所以析构函数不能重载。
一个类只有一个析构函数。写在public里面。
~student()
{cout<<"bye"<<endl;
}
何时使用析构函数
- 在一个函数内定义的对象,当这个函数结束时,自动执行析构函数 释放对象。
- 静态( static)局部对象要到main函数结束或执行exit命令时才自动执行析构函数释放对象。
- 全局对象(在函数外定义的对象)当main函数结束或执行exit命令 时自动执行析构函数释放对象。
- 如果用new建立动态对象,用delete时自动执行析构函数释放对象。
析构顺序
类似于栈:
最先调用构造函数的对象,最后调用析构函数。
- 在全局范围中定义的对象(在所有函数之外定义的对象),在文件中的所有函数(包括主函数)执行前调用构造函数。当主函数结束或执行exit 函数时,调用析构函数。
- 如果定义局部自动对象(在函数内定义对象),在创建对象时调用构造函数。如多次调用对象所在的函数,则每次创建对象时都调用构造函数。在函数调用结束时调用析构函数。
- 如果在函数中定义静态局部对象,则在第一次调用该函数建立对象时调用构造函数,但要在主函数结束或调用 exit 函数时才调用析构函数。
对象数组
对象是一个数组,依次执行成员函数。
#include<iostream>//还没有跑过
using namespace std;
class grade{
public:
stu(int c=2,int s=100):credit(c),score(s){}
int GPA;
private:
int credit;
int score;
int gross;
};
int grade::GPA(){
int total=0;
total=credit*score;
return (total/gross);
}
int
int main(){
grade s[3]={grade(5,78),
grade(2,92),
grade(3,86)};
gross=10;
cout<<s[0]<<endl;
}
指向对象的指针
// 定义pt是指向Time类对象的指针
Time *pt;
// 定义Time类对象t1
Time t1;
// 将对象t1的地址赋予pt
pt = &t1;
程序在此之后就可以用指针变量访问对象的成员。以下都可以访问指针pt指向的对象。
(*pt).hour
pt->hour
(*pt). show_time()
pt->show_time()
指向对象成员的指针
Time t1;
int * p1; // 定义一个指向整型数据的指针变量
p1 = & t1.hour; // 假定hour是公有成员
cout<< *p1
this 指针
不同的对象调用同一个成员函数 函数如何区分这些对象?
——每个成员函数中都包含一个特殊的this指针,指向本类对象的指针,当对象调用成员函数时,它的值就是该对象的起始地址。不同对象调用,this 指针指向不同。
C++ 通过编译程序,在对象调用成员函数时,把对象的地址赋予this 指针,用this 指针指向对象,实现了用同一个成员函数访问不同对象的数据成员。
int Box :: volume() {return ( height * width * length ) ; }
-> int Box :: volume( * this ){return (this->height*this->width * this->leng
*(this) 表示调用成员函数的对象
别处贴过来的一个理解:
当你进入一个房子后,你可以看见桌子、椅子、地板等,但是房子你是看不到全貌了。
对于一个类的实例来说,你可以看到它的成员函数、成员变量,但是实例本身呢?this是一个指针,它时时刻刻指向你这个实例本身——对象。
注意:
- const成员函数内部、构造函数内部、析构函数内部可以使用this指针
- 成员函数内的this指针指向成员函数所作用的对象
- 静态成员函数没有this指针,所以不能直接调用其它成员函数
常对象
把对象定义为常对象后,
数据成员:就是常变量,在定义时必须带实参作为数据成员的初值,在程序中不允许修改常对象的数据成员值。
成员函数:如果一个常对象的成员函数未被定义为常成员函数(除构造函数和析构函数外),则常对象不能调用这样的成员函数。
为了调用这种成员函数来访问常对象中的数据成员,要定义常成员函数: void get_time() const
mutable - 将数据成员的声明变为可变的,此时可以用声明为const的成员函数调用它。
const 的多种使用方式
对象的赋值
如果同一个类定义了两个或多个对象,则这些同类对象之间可以互相赋值。这里所指的对象的值含义是对象中所有数据成员的值
对象的复制
复制构造函数:
Box ::Box ( const Box & b )
//之前的复制构造是Box ::Box ( int h,int w,int l )
{
height = b.height;
width = b.width;
length = b.length;
复制构造函数只有一个参数:本类的对象,且采用引用对象形式,为了防止修改数据,加const限制。构造函数的内容就是将实参对象的数据成员值赋予新对象对应的数据成员,
调用复制构造函数有以下几种情况:
①程序中需要新建立一个对象,并用另一个对象对它初始化。
②函数的参数为类的对象。
③函数的返回值是类的对象。
静态对象 static
静态数据成员只有一份,由该类类型的所有对象共享访问。
理解:设某给类有 n个对象,这n个对象中的每一个静态成员在内存中共享一个整型数据空间。
如果某个对象修改了这个静态成员的值,其他 n-1个对象的 值也被改变。
也就是n个对象共享一个静态成员值
- 不能用构造函数为静态数据成员初始化,只能在类外专门对其初始化。如果未对静态数据成员赋初值,则编译系统自动赋初值0。
- 静态数据成员被类的所有对象共享,包括该类的派生类对象,基类对象和派生类对象共享基类的静态数据成员
- 对静态数据成员定义和初始化必须在类的外面,在类内只能声明。
- .静态数据成员可以作为成员函数的默认形参: static int a; void fun_1(int i = a);
静态成员函数
static 类型 成员函数( 形参){}
- 静态成员函数没有this指针,也就是说静态成员函数不能使用修饰符