类
类是我们自己定义的数据类型
类中包含成员函数和成员变量
访问类成员时,如果是类的对象可以使用对象名.成员名
进行访问;如果是指向对象的指针,可以使用指针名->成员名
进行访问
class student{
public:
int number;
char name[10];
};
int main(int argc, char const *argv[])
{
student stu;
stu.number = 12;
cout << stu.number << endl; //12
student *pstu;
pstu = &stu;
pstu->number = 15;
cout << stu.number << endl; //15
return 0;
}
public
成员提供类的接口,供外界调用,private
成员提供实现类功能的细节方法,外界无法使用这些成员。
对象拷贝
student stu;
student stu1 = stu;
student stu2(stu);
student stu3{stu};
student stu4 = {stu};
//这些都是对象的拷贝,每个成员变量逐个拷贝,在类中定义适当的赋值用算符,能够控制拷贝的行为
对象拷贝时,并不会调用传统意义上的构造函数,他们调用的是拷贝构造函数。
构造函数
在类中,有一种特殊的成员函数,他的名字与类名相同,我们在创建类的时候,这个特殊的函数就会被系统自动调用,这个成员函数就叫作构造函数。
构造函数的目的是初始化类对象的数据成员。
一个类中可以有多个构造函数,就可以为类对象的创建提供多种初始化方法,但是多个构造函数之间总要有点不同的地方,如参数的数量,参数的类型等
1)构造函数没有返回值,函数名前面什么都不用写
2)不可以手工调用构造函数,否则编译报错
3)正常情况下,构造函数应该被声明为public,因为我们创建一个对象时,系统要替我们调用构造函数
4)构造函数中,如果有多个参数,则我们创建对象时也要带上这些参数
//Time.h
class Time
{
public:
int hour;
int minute;
int second;
Time(int tmphour, int tmpminute, int tmpsecond);
Time(int tmphour, int tmpminute);
Time(int tmphour);
Time();
};
//Time.cpp
Time::Time(int tmphour, int tmpminute, int tmpsecond){
hour = tmphour;
minute = tmpminute;
second = tmpsecond;
}
Time::Time(int tmphour, int tmpminute){
hour = tmphour;
minute = tmpminute;
second = 0;
}
Time::Time(int tmphour){
hour = tmphour;
minute = 0;
second = 0;
}
Time::Time(){
hour = 0;
minute = 0;
second = 0;
}
// 创建类对象
Time mytime = Time(12,13,52);
Time mytime1(12,13,52);
Time mytime2{12,13,52};
Time mytime3 = {12,13,52}; //隐式类型转换
Time mytime4 = Time{12,13,52};
函数默认参数
规定:
1)默认值只能放在函数声明中,除非该函数没有函数声明
2)在具有多个参数的函数中指定默认值时,默认参数都必须出现在不默认参数的右边,一旦某个参数指定了默认值,则它右边的所有参数都必须指定默认值
隐式转换和explicit
当类有一个带有1个参数的构造函数时,容易被隐式调用,这样容易导致初始化错误。
class Time
{
public:
int hour;
int minute;
int second;
Time(int tmphour);
};
// 当类有一个带有1个参数的构造函数
Time::Time(int tmphour){
hour = tmphour;
minute = 0;
second = 0;
}
//可以初始化成功
Time mytime11 = 14;
Time mytime11 = (12,13,14,15,16); //16传入单参数构造函数
若构造函数声明中带有explicit,则这个构造函数只能用于初始化和显式类型转换。
class Time
{
public:
int hour;
int minute;
int second;
explicit Time(int tmphour);
};
Time::Time(int tmphour){
hour = tmphour;
minute = 0;
second = 0;
}
//不能初始化成功
Time mytime11 = 14; //Time mytime11 = Time(14);
Time mytime11 = (12,13,14,15,16); //Time mytime11 = Time{14};
一般单参数的构造函数,都要使用声明为explicit
,除非有特殊原因。
构造函数初始化类列表
实现
Time::Time(int tmphour, int tmpminute, int tmpsecond):hour(tmphour), minute(tmpminute), second(tmpsecond){}
初始化列表的执行在{}代码执行之前
hour、minute、second执行列表初始化的顺序,和在类中声明的顺序有关,与初始化列表代码的执行顺序无关
class Time
{
public:
int hour;
int minute;
int second;
Time(int tmphour, int tmpminute, int tmpsecond);
};
Time::Time(int tmphour, int tmpminute, int tmpsecond):second(tmpsecond), minute(tmpminute),hour(tmphour)
{
} //初始化的顺序为hour,minute,second
inline
在类定义中实现成员函数inline:类内的成员函数实现其实也叫做类内的成员函数定义,直接在类的定义中实现的成员函数,会被当作inline内联函数处理,能不能处理成功,取决于编译器。
成员函数末尾的const
在成员函数的末尾添加一个const,不但要在成员函数生声明中增加const,也要在成员函数定义中增加const。
作用是告诉系统,这个成员函数不会修改该对象里任何成员变量的值等等,这种函数也成为常量成员函数。
const成员函数,不管是不是const对象,都可以调用;非const成员函数,const对象不能调用,普通对象可以调用
普通函数后面不能加const
class Time
{
public:
int hour;
int minute;
int second;
void addhour(){};
void func1() const{
// hour = 1 错误,const成员函数不能改变成员变量的值
};
};
const Time a;
a.addhour(); //报错,addhour不是const成员函数
a.func1(); //可以调用
Time b;
b.addhour(); //可以调用
b.func1(); //可以调用
mutable
不稳定,容易改变的意思。mutable的引入是为了突破const的限制。
用mutable修饰的成员变量,表示这个成员变量永远处于可以被修改的状态,即使在const结尾的成员函数中,也可以修改。
class Time
{
public:
int hour;
int minute;
int second;
mutable int testvalue;
void addhour(){};
void func1() const{
testvalue = 1; //正确
};
};
返回自身对象的引用,this
class Time
{
public:
int hour;
int minute;
int second;
Time& addhour(int tmp);
Time& addminute(int minute);
};
Time& Time::addhour(int tmp){
hour += tmp;
return *this; //把对象自己返回
}
Time& Time::addhour(int minute){
this->minute += minute;
return *this; //把对象自己返回
}
Time a;
a.addhour(2).addhour(3) //可以这样使用
this
指针只能在成员函数中使用,全局函数,静态函数都不能使用this
指针
在普通成员函数中,this时指向非const对象的const指针,即为Time *const this
static成员
全局静态变量和局部静态变量
//a.cpp
int g_a = 15;
//b.cpp
extern int g_a;
cout<<g_a<<endl; //可以输出15
//a.cpp
static int g_a = 15; //限制该全局变量只能够用在本文件中
//b.cpp
extern int g_a;
cout<<g_a<<endl; //不可以输出15
// 局部静态变量
void func(){
static int abc = 5; //局部静态变量
//```
}
//静态局部变量存放在内存的全局数据区。函数结束时,静态局部变量不会消失,
//每次该函数调用时,也不会为其重新分配空间。它始终驻留在全局数据区,直到程序运行结束
//静态局部变量只在定义它的函数中可见,如果不为其显式初始化,则C++自动为其初始化为0。
静态成员变量,属于整个类的成员变量,不属于某个对象,属于整个类,一旦在某个对象中修改这个成员变量的值,在其他对象中直接能够看到修改的结果
对于这种成员变量的引用,使用类名::成员变量名
调用
成员函数前面也可以添加static
构成静态成员函数,使用类名::成员函数名
调用
一般在某个.cpp源文件的开头定义这个静态成员变量(分配内存),保证在调用任何函数之前这个静态成员变量已经被初始化。
class Time
{
public:
int hour;
int minute;
int second;
};
Time t1;
t1.hour = 11;
Time t2;
t2.hour = 12; // t1和t2的hour值不同
class Time
{
public:
static int hour; //静态成员变量的声明,还没有分配内存,不能初始化
int minute;
int second;
static void addhour(int tmp);
};
static void Time::addhour(int tmp){
}
int Time::hour =15; //可以不给初值,默认为0,分配内存,定义时不需要static
Time t1;
t1.hour = 11;
Time t2;
t2.hour = 12; // t1和t2的hour值相同,指向同一段内存地址
t1.addhour(1); //也可以t2.addhour(1); Time::addhour(1);
类内初始化
在c++11中,我们可以为类内成员变量提供一个初始值,那么我们在创建对象的时候,这个初始化值就用来初始化该成员变量
const成员变量的初始化,在构造函数的初始化列表里进行,不可以通过赋值来初始化
默认构造函数
没有参数的构造函数,我们就称为默认构造函数
如果没有构造函数,编译器就会为我们隐式的自动定义一个默认构造函数(无参数),称为合成的默认构造函数。
一旦写了一个构造函数,不管这个构造函数带几个参数,编译器都不会为我们创建合成的默认构造函数了
=default、=delete
c++11中引入
=default
编译器能够为我们自动创建函数体,带参数的构造函数不能使用
=delete
让程序员显式的禁用某个参数
class time{
public:
int hour;
int minute;
int second;
time() = default; //相当于inline
//time() = delete; //让系统不要创建默认构造函数
}
拷贝构造函数
默认情况下,类对象的拷贝是每个成员变量的逐个拷贝。
如果一个类的构造函数的第一个参数是所属类类型的引用,如果含有其他额外参数,且这些额外参数还都有默认值,则这个构造函数叫作拷贝构造函数,函数默认参数必须放在函数声明中,除非该函数没有函数声明。
一般第一个参数带const
拷贝构造函数一般不要声明成explicit
“成员变量的逐个拷贝” 的功能因为我们自己定义的拷贝构造函数的存在而失去作用,或者说,我们自己的拷贝构造函数取代了系统默认的“拷贝构造函数”
如果我们,没有定义拷贝构造函数,编译器会自动帮我们定以一个合成拷贝构造函数,实现“成员变量的逐个拷贝”的功能
每个成员的类型决定了它如何进行拷贝,比如说成员变量如果是整形,那么就直接把值拷贝过来;如果成员变量是类类型,那么就会调用这个类的拷贝构造函数来拷贝
拷贝够做函数的作用:会在一定时机,被系统自动调用
1)将一个对象作为实参传递给一个非引用类型的形参
2)从一个函数中返回一个对象
class Time
{
public:
int hour;
int minute;
int second;
Time();
Time(const Time &tmptime,int tmphour = 12); //拷贝构造函数,只能有一个
};
Time::Time(){
hour = 0;
minute = 0;
second = 0;
cout<<"Time()"<<endl;
}
Time::Time(const Time &tmptime,int tmphour){
hour = 0;
minute = 0;
second = 0;
cout<<"Time(Time &tmptime,int tmphour)"<<endl;
}
Time stu; // 调用默认构造函数
Time stu1 = stu; // 调用拷贝构造函数
Time stu2(stu); // 调用拷贝构造函数
Time stu3{stu}; // 调用拷贝构造函数
Time stu4 = {stu}; // 调用拷贝构造函数
Time stu5; // 调用默认构造函数
stu5 = stu4; //未调用拷贝构造函数
//输出
//Time()
//Time(Time &tmptime,int tmphour)
//Time(Time &tmptime,int tmphour)
//Time(Time &tmptime,int tmphour)
//Time(Time &tmptime,int tmphour)
//Time()
重载运算符
==,<,>,>=,<=,!=,++,--,+=,-=,+,-,cout,cin,<<,>>,=
重载指的是要写一个成员函数,这个函数名为operator 运算符
,这个成员函数体有程序员自己实现
有一些运算符,如果我们不自己写该运算符的重载,系统会自动给我们生成一个,比如说赋值运算符=
重载赋值运算符:有返回类型和参数列表
class Time
{
public:
int hour;
int minute;
int second;
Time();
Time(const Time &tmptime,int tmphour = 12); //拷贝构造函数,只能有一个
Time& operator = (const Time&);
};
Time::Time(){
hour = 0;
minute = 0;
second = 0;
cout<<"Time()"<<endl;
}
Time::Time(const Time &tmptime,int tmphour){
hour = 0;
minute = 0;
second = 0;
cout<<"Time(Time &tmptime,int tmphour)"<<endl;
}
Time& Time::operator = (const Time& tmp){
return *this;
}
拷贝赋值运算符
Time stu; // 调用默认构造函数
Time stu5; // 调用默认构造函数
stu5 = stu; //这个是赋值运算符,既没有调用默认构造函数,也没有调用拷贝构造函数
//调用了拷贝赋值运算符
析构函数
对象在销毁时,会自动调用析构函数
如果不写析构函数,编译器会自动生成一个默认的析构函数,默认的析构函数体为空{}.
析构函数也是类的成员函数,他的名字是~类名
,没有返回值,不接受任何参数,不能被重载,所以一个给定的类,只能有一个析构函数
构造函数的成员初始化:干了两件事,函数体之前初始化列表,函数体之中
析构函数的成员销毁:干了两件事,函数体之中,函数体之后销毁成员变量
构造函数中,先定义的变量先有值;析构函数中,先定义的函数后销毁
构造函数中使用了new
,析构函数中一定要使用delete
new对象时,也会调用构造函数,必须要自己释放,系统不会自动回收;在程序停止运行之前,一定要使用delete对象,使用delete对象时,会调用类的析构函数。
class Time
{
public:
int hour;
int minute;
int second;
Time(); //构造函数
~Time(); //析构函数
};
Time::Time(){
hour = 0;
minute = 0;
second = 0;
cout<<"Time()"<<endl;
}
Time::~Time(){
cout<<"~Time()"<<endl;
}
评论区