首页 > 编程语言 >C++ 面向对象程序设计 ---- 类2重点

C++ 面向对象程序设计 ---- 类2重点

时间:2024-07-18 19:26:36浏览次数:11  
标签:函数 int C++ ---- 面向对象 Date 拷贝 重载 构造函数

1.构造函数,代替Init()函数

构造函数的特点
1.函数名与类名相同
2.无返回值,void也不需要写
3.对象实例化时,系统会自动调用构造函数
4.构造函数可以重载

class Date
{
public:
    //函数名与类名相同,无返回值
    Date()//函数重载,无参
    {
        _year = 1;
        //..        
    }
    Date(int year)//函数重载,带参
    {
        _year = year;
        //...    
    }
    Date(int year = 2024,...)//函数重载,缺省,各种缺省的重载都可以
    {
            
    }
    //全缺省和无参不可以同时存在,因为虽然构成了重载,但调用函数会出现歧义
    void Dateprint()
    {
        //...    
    }
private:
    int _year;
    //...
};
int main()
{
    Date A;//调用无参的构造函数,A后面不跟括号!!!
    Date B(2023);//调用带参的
    //如果写成Date A();//与函数声明混淆,规定不可以这么写
    //这里这么实例化之后系统就会自动调用构造函数
    return 0;
}

5.如果类中没有显式地定义构造函数,C++编译器会自动生成一个无参地默认构造函数,如果显式定义则编译器不再生成
6.默认构造函数包括:显式定义的无参构造函数,全缺省的构造函数,编译器自动生成的构造函数,总结就是不需要传实参调用的构造函数就是默认构造函数

我们不写,编译器生成的默认构造函数对于内置类型成员变量是否初始化不确定,看编译器,VS没有初始化
类中的自定义类型的成员变量会调用这个自定义类型的默认构造函数初始化,比如两个栈实现自己的队列这种题,如果这个成员变量没有默认构造,那么就会报错,需要使用初始化列表解决

#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
    Stack(int n = 4)
    {
        _a = (STDataType*)malloc(sizeof(STDataType) * n);
        if (nullptr == _a)
        {
            perror("malloc申请空间失败");
            return;
        }
        _capacity = n;
        _top = 0;
    }
private:
    STDataType* _a;
    size_t _capacity;
    size_t _top;
};
// 两个Stack实现队列
class MyQueue
{
public:
    //编译器默认⽣成MyQueue的构造函数调⽤了Stack的构造,完成了两个成员的初始化
private:
    Stack pushst;//自定义类型
    Stack popst;
};
int main()
{
    MyQueue mq;
    return 0;
}

总结:大多数情况下,构造函数都需要我们自己写,少数情况比如MyQueue 通过 Stack的默认构造函数自动实现

2.析构函数,代替Destroy()函数完成 对象中资源清理工作

析构函数特点:
1.类名前加"~"
2.无参数无返回值,同样不需要加void
3.一个类只能有一个析构函数,若未显式定义,编译器会自动生成一个默认的析构函数
4.对象生命周期结束时,系统会自动调用析构函数

#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:
    Stack(int n = 4)
    {
        _a = (STDataType*)malloc(sizeof(STDataType) * n);
        if (nullptr == _a)
        {
            perror("malloc申请空间失败");
            return;
        }
        _capacity = n;
        _top = 0;
    }
    ~Stack()
    {
        cout << "~Stack()" << endl;
        free(_a);//资源清理
        _a = nullptr;
        _top = _capacity = 0;
    }
private:
    STDataType* _a;
    size_t _capacity;
    size_t _top;
};
// 两个Stack实现队列
class MyQueue
{
public:
//编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源
// 显⽰写析构,也会⾃动调⽤Stack的析构
/*~MyQueue()
{}*/
private:
    Stack pushst;
    Stack popst;
};
int main()
{
    Stack st;
    MyQueue mq;
return 0;
}

Date这种没有资源申请的类不需要写析构函数,因为就没有资源需要清理
析构函数的调用顺序:
规定:如果有多个对象,后定义的先析构
在这里插入图片描述
5.如果不显式地写析构函数,编译器会自动生成默认的析构函数,默认的析构函数对内置类型成员不做处理,对自定义类型成员会调用它的析构函数
6.如果显式地写了析构函数,对于自定义类型成员也会自动调用它的析构函数,也就是不论什么情况,自定义类型的成员一定会调用自己的析构函数
7.如果没有资源申请,可以自己不用写析构函数如Date;如果有资源申请,那就一定要自己写析构函数,否则会内存泄漏

3.拷贝构造函数

拷贝构造函数是特殊的构造函数,也就是构造函数的一个重载.作用是通过初始化定义的方式拷贝一个已经实例化的对象
特点:
1.如果构造函数的第一个参数是自身类型的引用,且任何额外的参数都有默认值,则这个构造函数就是拷贝构造函数
2.C++规定自定义类型的(类)函数传值传参(拷贝行为)要调用拷贝构造,因此建议自定义类型的函数建议就用传引用传参,因为传值也需要调用一次拷贝构造,不如就传引用,高效

拷贝构造函数的第一个参数必须是自身类类型的引用,如果是传值就会引发无限递归报错.

如果拷贝构造函数的第一个参数是传值就会发生下图这样的无限递归

在这里插入图片描述
3.未显式定义的拷贝构造,编译器会自动生一个拷贝构造函数,这个自动生成的拷贝构造函数对内置类型成员变量会进行简单的值拷贝/浅拷贝(一个字节一个字节的拷贝)

#include<iostream>
using namespace std;
class Date
{
public:
    Date(int year = 1, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
// 编译报错:error C2652: “Date”: ⾮法的复制构造函数: 第⼀个参数不应是“Date”
//Date(Date d)
    Date(const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    Date(Date* d)
    {
        _year = d->_year;
        _month = d->_month;
        _day = d->_day;
    }
    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};
void Func1(Date d)
{
    cout << &d << endl;
    d.Print();
}
// Date Func2()
Date& Func2()
{
    Date tmp(2024, 7, 5);
    tmp.Print();
    return tmp;
}
int main()
{
    Date d1(2024, 7, 5);
// C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,所以这⾥传值传参要调⽤拷⻉
构造
// 所以这⾥的d1传值传参给d要调⽤拷⻉构造完成拷⻉,传引⽤传参可以较少这⾥的拷⻉
    Func1(d1);
    cout << &d1 << endl;
// 这⾥可以完成拷⻉,但是不是拷⻉构造,只是⼀个普通的构造
    Date d2(&d1);
    d1.Print();
    d2.Print();
//这样写才是拷⻉构造,通过同类型的对象初始化构造,⽽不是指针
    Date d3(d1);
    d2.Print();
// 也可以这样写,这⾥也是拷⻉构造
    Date d4 = d1;
    d2.Print();
// Func2返回了⼀个局部对象tmp的引⽤作为返回值
// Func2函数结束,tmp对象就销毁了,相当于了⼀个野引⽤
    Date ret = Func2();
    ret.Print();
    return 0;
}

4.像Date这样的类,全是内置类型且没有指向任何资源,编译器自动生成的拷贝构造就可以满足我们的需求,他会把变量值拷贝,这就达到了目的.而像栈stack这种虽然也全是内置类型,但是_a指向了资源,仅仅是编译器默认生成的拷贝构造的浅拷贝就不满足我们的要求了,这需要自己实现一个深拷贝

简单的说就是,浅拷贝非常的死板,完完全全就是一个字节一个字节地拷贝,如果像stack这种指向了了资源的类,拷贝的时候也会死板地把被拷贝对象的所有涉及地址拷贝地一模一样,那么这个拷贝出来的对象和原来的对象就会有"交汇"的东西,总不能修改这个对象,另一个对象也改,同时也会带来一个问题,最后析构的时候,释放的空间也是同一块,这回导致程序崩溃!
在这里插入图片描述
在这里插入图片描述
两个stack实现Myqueue:
对于类里面自定义类型的成员变量(类里面还有个类对象),如果该类没有自己实现拷贝构造,Myqueue自动调用的拷贝构造会自动调用stack实现的拷贝构造,那么Myqueue不用自己写拷贝构造也可以实现深拷贝了,因为stack都写好了.
5.自定义类型的传值返回会产生一个临时对象带调用拷贝构造,
传引用返回会返回对象的别名(没有产生拷贝)
在这里插入图片描述
但是返回对象是一个函数局部作用域的局部对象,函数结束就销毁了,那么返回它的引用是有问题的
在这里插入图片描述
传引用返回可以减少拷贝,但是要确保一定要返回对象,在当前函数结束后,对象还在.

4.赋值运算符重载

运算符重载:

1.C++规定类类型的对象使用运算符时必须转换成调用对应运算符重载,原有的运算符对于自定义的类没有意义,会报错.
2.运算符重载是具有特殊名字的函数 比如operater+ operater-…
3.参数个数和该运算符的操作数个数一样多,一元运算符有一个参数,二元有两个,而且顺序从左至右
4.如果重载成成员函数(定义在类内部),则默认的第一个参数就是this指针,参数会看起来少一个
5.重载以后的运算符作用优先级结合性不变
6. .(指针进行成员函数回调时会使用) ::(域作用) sizeof(计算数据类型大小) ? :(三目操作符) . 这五个运算符不能重载
对于 .
C++规定 普通函数的函数名就是函数指针,但是需要使用&才能取到函数指针
在这里插入图片描述
7.重载操作符必须要有一个类类型的参数
比如 int operater+(int x,int y) 不能运算符重载实现内置类型对象操作,这样会让+ 变成- ,变成其他含义

赋值运算符重载

1.赋值运算符重载是一个默认的成员函数,用于完成两个已经存在的对象的拷贝赋值,区别拷贝构造的是一个对象拷贝初始化给另一个要创建的对象
2.是运算符重载,规定必须为成员函数,参数建议写成const当前类类型引用,否则会传值调用调用拷贝构造
3.没有显式实现时,编译器会默认生成一个赋值运算符重载,默认赋值运算符重载的行为和默认拷贝构造类似,对内置类型浅拷贝/值拷贝,对自定义类型会调用它的赋值重载.

class Date
{
public:
    Date(int year = 1, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    Date(const Date& d)
    {
        cout << " Date(const Date& d)" << endl;
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    // 传引⽤返回减少拷⻉
    // d1 = d2;
    Date& operator=(const Date& d)
    {
        // 不要检查⾃⼰给⾃⼰赋值的情况
        if (this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        // d1 = d2表达式的返回对象应该为d1,也就是*this
        return *this;//实现d1 = d2 = d3
    }
    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    Date d1(2024, 7, 5);
    Date d2(d1);
    Date d3(2024, 7, 6);
    d1 = d3;
    // 需要注意这⾥是拷⻉构造,不是赋值重载
    // 请牢牢记住赋值重载完成两个已经存在的对象直接的拷⻉赋值
    // ⽽拷⻉构造⽤于⼀个对象拷⻉初始化给另⼀个要创建的对象
    Date d4 = d1;
    return 0;
}

4.同拷贝构造,Date不需要自己写赋值重载,Stack有指向资源,需要自己写.
Myqueue这种里面是自定义类型的,不需要自己写复制重载,对于自定义类型会自动调用自定义类型(Stack)的赋值重载

标签:函数,int,C++,----,面向对象,Date,拷贝,重载,构造函数
From: https://blog.csdn.net/2302_80453405/article/details/140530872

相关文章

  • Self-Supervised Learning for Point Clouds Data: A Survey
    摘要综述了自监督学习(SSL)在3D点云数据处理领域的最新进展,对现有SSL方法进行了细致的分类和评估,并在多个基准数据集上对代表性方法进行了性能比较。同时指出了现有研究的局限性,提出了未来研究的方向。Introduction文章主要是针对自监督学习的(SSL),详细阐述了3D点云数据由于其......
  • Self-supervised Learning for Pre-Training 3D Point Clouds: A Survey
    Abstract点云数据由于其紧凑的形式和表示复杂3D结构的灵活性而被广泛研究。点云数据准确捕获和表示复杂3D几何形状的能力使其成为广泛应用的理想选择,包括计算机视觉,机器人技术和自动驾驶,所有这些都需要了解底层空间结构。这种方法旨在从未标记的数据中学习通用和有用的点云表......
  • Python学习之推导式
    目录一、列表推导式二、集合推导式三、字典推导式四、元组推导式一、列表推导式[expressionforiteminiterableifcondition]介绍:(1)expression:生成元素的表达式。(2)item:可迭代对象中的每个元素。(3)iterable:可迭代对象,如列表、元组、字符串等。(4)condition(可选):筛选......
  • 【永劫无间】
    永劫无间介绍《永劫无间》是由网易旗下“24Entertainment工作室”开发的一款多人动作竞技游戏,于2021年7月8日开启不删档测试,《永劫无间》Steam全球公测于2021年8月12日10:00正式开服,同时国服与Steam平台同步上线S1浪潮赛季。EPIC平台上线时间将延期至9月中旬。游戏以......
  • 揭秘Java世界:轻松检测两个List是否有交集
    哈喽,大家好,我是木头左!快速入门:什么是List的交集?在Java中,当提到两个List的交集,指的是这两个列表共有的元素集合。例如,如果有两个List,其中一个包含元素A,B,C,另一个包含B,C,D,那么它们的交集就是B,C。理解了这一点,就可以开始探讨如何检测这个交集,以及它对编程实践的意义。Ja......
  • stm32 - IIC
    目录STM32-IIC1.基本概念2.引脚说明SDASCK/SCL3.传输方向4.通信过程1.空闲状态2.开始信号3.数据发送4.应答信号5.数据接收6.停止信号STM32-IIC1.基本概念半双工同步通信的串行通信接口2.引脚说明SDASCK/SCLIIC总线只需要两根引脚就可以实现通信,一根是数......
  • 点对(强连通)
    https://www.luogu.com.cn/problem/P4306第2题   点对 查看测评数据信息给定一个N个节点的有向图,统计该有向图的点对个数,如下图:顶点1可达1,2,3,4,5顶点2可达2,3,4,5顶点3可达3,4,5顶点4,5都只能到达自身。所以这张图的点对数为14.给定一张图,请你求出它的点对数对......
  • Java中interface的default和static方法
    Java中interface的default和static方法Java中interface的default和static方法完整代码及其运行结果从Java8开始:static方法:接口不能通过实例调用static方法接口中的static方法不能被继承子类不能继承接口的static方法,可以继承、不能覆写父类的static方法。default......
  • 遍历(强连通)
    https://www.luogu.com.cn/problem/P2194第3题   遍历 查看测评数据信息给定n个点,m条边的有向图,每个节点有一个权重v(i),你的任务是把n个点遍历一遍,点可以重复经过。允许的操作如下:每次你可以选择一个开始节点i,如果可以从i出发,最后可以回到i节点,那么你的花费为v(i)。问:最......
  • iOS开发基础133-GCD相关
    先看一段代码,这是项目中图片上传的一部分代码。//开启线程组上传图片dispatch_group_tgroup=dispatch_group_create();[self.selectedPhotosenumerateObjectsUsingBlock:^(UIImage*_Nonnullobj,NSUIntegeridx,BOOL*_Nonnullstop){dispatch_gro......