【C++】类的默认成员函数(上)

在这里插入图片描述

类的默认成员函数

1构造函数

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值 ,并且在对象的生命周期内只调用一次。

1.1构造函数特征

构造函数是特殊的成员函数,其主要的功能是初始化对象。特征:

  • 1.函数名和类名相同
  • 2.无返回值,且返回类型不能写void
  • 3.对象实例化时编译器自动调用对应的构造函数
  • 4.构造函数可以重载
class Date
{
    public:
    	//无参数构造函数
    	Date()
        {
            
        }
    	//带参构造函数
    	Date(int year,int month,int day)
        {
            _year=year;
            _month=month;
            _day=day;
        }
    	void print()
        {
            cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
        }
    private:
    	int _year;
    	int _month;
    	int _day;
};
int main()
{
    Date d1;//调用无参数构造函数
    d1.print();
    Date d2(2022,5,15);//调用带参数的构造函数
    d2.print();
    //需要注意的是,调用无参数构造函数时,对象后面不能加上括号,否则编译器会认为是函数声明
    // Date d3(); 这种写法存在歧义
    return 0;
}

image-20220515222742814

1.2编译器自动生成的构造函数

如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成

class Date
{
    public:
    	//编译器会自动生成一个无参的构造函数
    	void print()
        {
            cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
        }
    private:
    	int _year;
    	int _month;
    	int _day;
};
int main()
{
    Date d1;
    d1.print();
    return 0;
}

image-20220515223200775

1.3编译器默认的构造函数

构造函数把成员变量分为两种类型

  • 内置类型/基本类型:int/char/double/ptr…
  • 自定义类型class/struct定义的类型对象。
  • 无参的构造函数、全缺省的构造函数、用户不写编译器默认生成的构造函数都被称为默认构造函数
  • 默认生成的构造函数对应内置类型成员变量不做处理,对于自定义类型成员变量才会处理
  • 无参的构造函数和全缺省的构造函数只能存在一个,否则会发生在构造对象时会出现歧义。

上面的可能不太好理解,我们用下面的程序来解释

程序1

class Date
{
public:
	Date()
	{
		_year = 1900;
		_month = 1;
		_day = 1;
	}
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	return 0;
}

该程序编译无法通过,构造d1时编译器不知道调用无参的构造函数还是全缺省的构造函数

程序2

class A
{
public:	
	A()
	{
		cout << " A()" << endl;
		_a = 0;
	}
private:
	int _a;
};
class Date
{
public:
void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year; 
	int _month; 
	int _day;
	A _aa;
};
int main()
{
    Date d1;
    return 0;
}

image-20220515224954123

程序3

class A
{
public:
	A(int a)
	{
		_a = a;
	}
private:
	int _a;
};
class Date
{
public:
private:
	A _aa;
	A _bb;
};
int main()
{
	Date d1;
	A ca;
	return 0;
}

image-20220515233319067

Date成员变量是两个A类型的变量,所以Date的默认构造函数是需要A调用自己的构造函数

而A定义了带参的构造函数,不存在默认构造函数,所以Date的默认构造函数无法被调用。

//修改方式
class A
{
public:
    //或者A()或者A(int a=10)
private:
	int _a;
};
class Date
{
public:
private:
	A _aa;
	A _bb;
};
int main()
{
	Date d1;
	A ca;
	return 0;
}
1.4C++11特征

在C++发展的过程中,有些大佬也发现只处理自定义类型的成员变量不方便,为了向下兼容又要对缺点进行改正,C++的大佬们打了个补丁

class A
{
public:
	A(){
	_a = 0;
	}
private:
	int _a;
};
class Date
{
public:
private:
	A _aa;
	A _bb;
	int _ca=10;
};
int main()
{
	Date d1;
	return 0;
}

image-20220515234506055

在这里插入图片描述

2析构函数

与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的资源清理工作

2.1特征
  • 1.析构函数名是在类名前加上字符~
  • 2.无参数返回值,会构造函数一样
  • 3.一个类有且只有一个析构函数:因为析构函数无参数所以无法进行函数重载
  • 4.对象生命周期结束时,C++编译器自动调用析构函数
class arr
{
    public:
    	arr(int capacity=10)
        {
            _a=(int*)malloc(sizeof(int)*capacity);
            assert(_a);
            _capacity=capacity;
            _size=0;
        }
    	~arr()
        {
            cout<<"free()"<<endl;
            free(_a);
            _a=nullptr;
            _capacity=_size=0;
        }
    private:
    	int _capacity;
    	int _size=0;
    	int* _a;
};

image-20220516001527249

2.2编译器默认的析构函数

和构造函数一样,析构函数也把成员变量分为两种类型;对于内置类型析构函数不做处理,自定义类型析构函数会调用自定义类型的默认析构函数。

class arrs
{
public:
    arrs(int sizes = 2)
    {
        _size= sizes;
    }
private:
    arr ar1;
    arr ar2;
    int _size;
};

int main()
{
    arrs arrs1;
    return 0;
}

image-20220516002320959

析构函数的调用顺序

int main()
{
    arr arr1(5);
    arr arr2(5);
    return 0;
}

函数栈帧是在虚拟进程地址空间中的栈区建立,满足先创建后销毁的规则

image-20220516003001991

在这里插入图片描述

3拷贝构造函数

构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用

3.1特征
  • 拷贝构造函数是构造函数的重载形式
  • 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。

关于传值方式会引发无穷递归调用,我也是想了一段时间才理清楚调用的方式和传递过程:

**C++中规定,对于自定义类型传参需要先进行拷贝构造,而拷贝构造需要传递参数…**不断的套娃,出现死递归

//如果使用传值传参
class Date
{
    public:
    	Date(int year=2022,int month=5,int day=15)
        {
            _year=year;
            _month=month;
            _day=day;
        }
    	Date(Date date)
        {
            _year=date._year;
            _month=date._month;
            _day=date._date;
        }
    private:
    	int _year;
    	int _month;
    	int _day;
}

image-20220516011030525

//正确写法
class Date
{
    public:
    	Date(int year=2022,int month=5,int day=15)
        {
            _year=year;
            _month=month;
            _day=day;
        }
    	Date(const Date&date)
        {
            _year=date._year;
            _month=date._month;
            _day=date._date;
        }
    private:
    	int _year;
    	int _month;
    	int _day;
}
3.2编译器默认生成的拷贝构造函数

若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝方式为浅拷贝。

class Date
{
    public:
    	Date(int year=2022,int month=5,int day=15)
        {
            _year=year;
            _month=month;
            _day=day;
        }
    private:
    	int _year;
    	int _month;
    	int _day;
};
int main()
{
    Date d1(2021,5,16);
    Date d2(d1);
    return 0;
}

image-20220516011653380

浅拷贝的方式缺点是只适合对不包含指针的对象进行拷贝

class stack
{
public:
	stack(int capacity = 10)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		assert(_a);
		_capacity = capacity;
		_top = 0;
	}
	~stack()
	{
		cout << "~stack()" << endl;
		free(_a);
		_a = nullptr;
		_capacity = _top = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	stack st1;
	stack st2(st1);
	return 0;
}

image-20220516012400809

原因:如果使用编译器默认的拷贝构造函数,那么默认的拷贝构造函数对象按内存存储按字节序完成拷贝。

而stack类有int _a成员变量,在浅拷贝中,只是把str1._a指针传递给了str2._a。所以两个变量的指针是相同的!!!*

因此两次析构函数释放的是同一个空间,所以会出现运行错误

题目:
设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?( )

C c;
int main()
{
A a;
B b;
static D d;
return 0}

解析:因为d是static静态变量,c是全局变量,两者都存放在静态区;在main函数调用完被销毁;又因为函数栈帧在栈区,所以顺序为 b,a,d,c
在这里插入图片描述

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

影中人lx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值