侧边栏壁纸
博主头像
Into The Abyss 博主等级

My Life is a Death Race

  • 累计撰写 34 篇文章
  • 累计创建 7 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

c++_study_class

Administrator
2023-10-18 / 0 评论 / 0 点赞 / 174 阅读 / 0 字

类是我们自己定义的数据类型

类中包含成员函数和成员变量

访问类成员时,如果是类的对象可以使用对象名.成员名进行访问;如果是指向对象的指针,可以使用指针名->成员名进行访问

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;
}
0
c++

评论区