C++入门小馆:类和对象(二)

嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的passion。准备好和我一起冲进代码的奇幻宇宙了吗?Let's go!

 

我的博客:yuanManGan

我的专栏:C++入门小馆 C言雅韵集 数据结构漫游记  闲言碎语小记坊 题山采玉

 

目录

1.类的默认成员函数:

2.构造函数:

3.析构函数

4.拷贝构造函数


 

 

这里我们就先只介绍四个简单的默认成员函数

1.类的默认成员函数:

一个类,如果你没有显式定义默认成员函数的话,编译器会自动调用下面这几个默认成员函数。

我们需要了解到

第一:我们未显式定义时,系统给我们定义的函数的行为是什么,是否满足我们的需求。

第二:如果默认生成的函数不满足我们的需求,我们该怎么写一个满足我们需求的默认成员函数。

2.构造函数:

构造函数的作用不是构造空间,而是将类中的成员变量初始化,在定义类变量时,即栈帧创建时以及创建了。

我们的构造函数需要满足以下特点:

1.函数名与类名相同

2.函数无返回值,注意这里是无返回值,不写返回值,不写void

当然构造函数也可以函数重载

#include<iostream>
using namespace std;

class Date
{                                
public:
        //构造函数
        Date(int year, int month, int day)
        {
            _year = year;
            _month = month;
            _day = day;        
        }
        //全缺省
        Date(int year = 1, int month = 1, int day = 1)
        {
            _year = year;
            _month = month;
            _day = day;        
        }
        void Print()
        {
            cout << _year << "-" << _month << "-" << _day << endl;
        }                         
private:
    int _year;
    int _month;
    int _day;       
};
int main()
{
    Date d1;
    Date d2(2025, 3, 11);
    
    d1.print();
    d2.print();
    return 0;
}

在使用传参实现构造时,要加上括号和要传入的参数,但如果不需要传参时,我们不能只要括号不要参数,会让人误解这是

Date d1();

全缺省和无缺省以及默认函数不能同时存在,会出现歧义。

大多数的类都需要我们自己写函数都需要我们写构造函数,确定初始化方式。

如果我们不写,编译器对内置类型的初始化没有要求,就是说是否进行初始化是不确定的。

而对应自定义类型做成员变量时,如果该成员变量没有构造函数,编译器就会报错。

少数Myqueue类型会自动调用内置类型里面的构造函数。

#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;
}

3.析构函数

不是对对象本身的销毁,比如局部对象是存在栈帧的,函数结束栈帧销毁,他就释放了。他是完成资源清理工作,类似于destroy功能。一般在我们在类的内部开辟空间时,最后要使用析构函数进行销毁空间。

比如我们之前学习的Date类就不需要析构函数

1.析构函数函数名是在类名前加字符~。

2.无返回值。

3.对象生命周期结束时,系统会自动调用析构函数。

同样Myqueue类型会自动调用内置类型里面的析构函数

#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的构造,完成了两个成员的初始化
private:
    Stack pushst;
    Stack popst;
};
int main()
{
    MyQueue mq;
    return 0;
}

有了这两个函数我们就不用担心忘记初始化以及销毁了。

4.拷贝构造函数

1.是构造函数的一个重载。

2.拷贝构造函数的第一个参数必须是类类型对象的引用,传值方式编辑器会报错。

Date d2(d1); //传值传参 //传值返回

如果我们不写拷贝构造函数,默认生成的拷贝构造函数会将我们的内置类型进行值拷贝(浅拷贝),对自定义类型去调用它的拷贝构造。

如果我们拷贝构造函数传入的参数写的不是引用类型,会出现死循环。

传值传参要调⽤拷⻉构造

#include<iostream>
using namespace std;

class Date
{                                
public:
        //构造函数
        Date(int year = 1, int month = 1, int day = 1)
        {
            _year = year;
            _month = month;
            _day = day;        
        }
        //拷贝构造函数
        Date(const &Date d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;        
        }
        void Print()
        {
            cout << _year << "-" << _month << "-" << _day << endl;
        } 
        void Func1(Date d)
        {
            cout << &d << endl;
            d.Print();
        }
        // Date Func2()
        Date& Func2()//野引用
        {
            Date tmp(2024, 7, 5);
            tmp.Print();
            return tmp;
        }                        
private:
    int _year;
    int _month;
    int _day;       
};
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;
}

 

#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(const Stack& st)
    {
        // 需要对_a指向资源创建同样⼤的资源再拷⻉值
        _a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);
        if (nullptr == _a)
        {
            perror("malloc申请空间失败!!!");
            return;
         }
        memcpy(_a, st._a, sizeof(STDataType) * st._top);
        _top = st._top;
        _capacity = st._capacity;
    }
    void Push(STDataType x)
    {
        if (_top == _capacity)
        {
            int newcapacity = _capacity * 2;
            STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
            sizeof(STDataType));
            if (tmp == NULL)
            {
                perror("realloc fail");
                return;
            }
            _a = tmp;
            _capacity = newcapacity;
        }
        _a[_top++] = x;
    }
    ~Stack()
    {
        cout << "~Stack()" << endl;
        free(_a);
        _a = nullptr;
        _top = _capacity = 0;
    }
private:
    STDataType* _a;
    size_t _capacity;
    size_t _top;
};

// 两个Stack实现队列
class MyQueue
{
public:
private:
    Stack pushst;
    Stack popst;
};
int main()
{
    Stack st1;
    st1.Push(1);
    st1.Push(2);
    // Stack不显⽰实现拷⻉构造,⽤⾃动⽣成的拷⻉构造完成浅拷⻉
    // 会导致st1和st2⾥⾯的_a指针指向同⼀块资源,析构时会析构两次,程序崩溃
    Stack st2 = st1;
    MyQueue mq1;
    // MyQueue⾃动⽣成的拷⻉构造,会⾃动调⽤Stack拷⻉构造完成pushst/popst
    // 的拷⻉,只要Stack拷⻉构造⾃⼰实现了深拷⻉,他就没问题
    MyQueue mq2 = mq1;
    return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值