admin管理员组文章数量:1029890
C++: 类和对象(中)
类的默认成员函数
构造函数
定义
代码语言:javascript代码运行次数:0运行复制#include<iostream>
using namespace std;
class Date
{
public:
void Init(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.Init(2024, 7, 20);
d1.Print();
return 0;
}
Date类型实例化时,每次都要调用Init进行初始化,这未免有点麻烦,C++则有构造函数,构造函数完美的替代了Init。
构造函数是一个特殊的成员函数,名字与类名相同, 创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。
特征
- 1. 函数名与类名相同。
- 2. ⽆返回值。(返回值啥都不需要给,也不需要写void,不要纠结,C++规定如此)
- 3. 对象实例化时系统会⾃动调⽤对应的构造函数。
- 4. 构造函数可以重载。
- 5. 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦用户显示式定义编译器将不再⽣成。
分类
无参构造函数
注意
代码语言:javascript代码运行次数:0运行复制#include<iostream>
using namespace std;
class Date
{
public:
// 1.⽆参构造函数
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;//正确的
Date d2();//错误的
d1.Print();
return 0;
}
要调用无参构造函数的时候,那么写 Date d2()编译器会有警告,不知道这是函数还是构造,使用这个写法是错误的。
带参构造函数
代码语言:javascript代码运行次数:0运行复制#include<iostream>
using namespace std;
class Date
{
public:
// 2.带参构造函数
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(2024,7,20);
d1.Print();
return 0;
}
全缺省构造函数
代码语言:javascript代码运行次数:0运行复制#include<iostream>
using namespace std;
class Date
{
public:
// 3.全缺省构造函数
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;
d1.Print();
return 0;
}
注意:无参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数,都叫做默认构造函数。 但是这三个函数有且只有⼀个存在,不能同时存在。
无参构造函数和全参构造函数虽然构成重载函数,但是调用时会发生歧义
不写时,编译器默认生成0的
对内置类型成员变量,是否初始化是不确定的,取决于编译器
对自定义类型成员变量,要求调用这个成员变量的默默人构造函数初始化。没有默认构造函就会报错,要初始化这个成员变量,需要用到初始化列表
初始化列表
除了以上的构造函数,在初始化成员变量主要使⽤函数体内赋值,构造函数还有另一种方式,就是初始化列表。结构如下
代码语言:javascript代码运行次数:0运行复制ClassName::ClassName(Type1 arg1, Type2 arg2)
:member1(arg1)
, member2(arg2)
{
// 构造函数体
}
- 每个成员变量在初始化列表中只能出现⼀次,语法理解上初始化列表可以认为是每个成员变量定义 初始化的地⽅。
- 引⽤成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始 化,否则会编译报错。
- C++11⽀持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显⽰在初始化列表初始化的 成员使⽤的。
- 优先使用初始化列表:尽量通过初始化列表显式初始化成员变量,这样可以避免未初始化的风险并提高代码的明确性。
- 内置类型的初始化问题:内置类型成员变量如果没有初始化,将导致不确定行为。因此,尽量避免依赖编译器的默认行为,明确初始化。
- 自定义类型成员的初始化要求:如果成员是自定义类型并且没有默认构造函数,在没有在初始化列表中进行初始化时,会导致编译错误。
- 初始化列表中是按照成员变量在类中的声明顺序进行初始化的,跟成员变量在初始化列表的顺序无关。
举例:
代码语言:javascript代码运行次数:0运行复制class Date
{
Date(int year = 1,int month = 1,int day = 1)
:_year(year)
,_month(month)
,_day(day)
{}
private:
int _year;
int _month;
int _day;
};
析构函数
定义
构造函数和析构函数有具有相反的作用。
析构函数:析构函数是一个特殊的函数,当对象被销毁时自动调用。它的名称与类名相同,但前面有一个波浪号(~),析构函数用于清理对象占用的资源。
代码语言:javascript代码运行次数:0运行复制class Date
{
public:
Date(int year,int month, int day)
{
cout << " Date(int year, int month, int day) " << endl;
_year = year;
_month = month;
_day = day;
}
~Date()
{
cout << " ~Date()" << endl;
}
private:
int _year;
int _month;
int _day;;
};
int main()
{
Date d1(2024,7,7);
return 0;
}
特征
1. 析构函数名是在类名前加上字符~。
2. ⽆参数⽆返回值。(这⾥跟构造类似,也不需要加void)
3. ⼀个类只能有⼀个析构函数。若未显式
代码语言:javascript代码运行次数:0运行复制#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;
}
private:
int _year;
int _month;
int _day;
};
定义,系统会⾃动⽣成默认的析构函数。
4. 对象⽣命周期结束时,系统会⾃动调⽤析构函数。
5. 跟构造函数类似,我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理,⾃定类型成员会 调⽤他的析构函数。
注意:对于⾃定义类型成员也会调⽤他的析构,也就是说⾃定义类型成员⽆论什么情况都会⾃动调⽤析构函数。
如果类中没有申请资源时,析构函数可以不写,直接使⽤编译器⽣成的默认析构函数,如Date;(上面的代码只是作为例子,~Date可以不写,编译器会自动生成)
拷贝构造函数
定义
拷贝构造函数:拷贝构造函数是一个特殊的构造函数,用于创建一个新对象,其内容完全相同于另一个已存在的对象。
特征
1、拷贝构造函数是构造函数的一个重载
2、拷贝构造函数的第一个参数必须是类类型对象的引用。
3、C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,所以这⾥⾃定义类型传值传参和传值返 回都会调⽤拷⻉构造完成。
代码语言:javascript代码运行次数:0运行复制#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) //error 引发无穷递归
Date(const Date& d) //拷贝构造函数
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024,10,10);
Date d2(d1); //拷贝构造
return 0;
}
若未显式定义拷⻉构造,编译器会⽣成⾃动⽣成拷⻉构造函数。⾃动⽣成的拷⻉构造对内置类型成 员变量会完成值拷⻉/浅拷⻉
赋值运算符重载
运算符重载
C++预定义的运算符,入“+""-"等,其操作对象只能是基本数据类型。在表达式中看到 “*” “+”时,C++对给定数据结构进行相乘,相加的运算,然后得出结果,当然,使用函数也是可以,类也能进行这样的操作,但语法就复杂多了。
那么,就可以多已有的运算符赋育新的含义,化繁为简,利用运算符来操作对象。
运算符重载是具有特殊函数名的函数 ,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名: 返回类型 operator 运算符 (参数类
型1 参数名1, 参数类型2 参数名2)
注意:
- 不能通过连接语法中没有的符号来创建新的操作符:⽐如operator@。
- 重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。一元运算符有一个参数,二元运算符有两个参数(左侧运算对象传给第一个参数,右侧传给第二个参数)。
- 如果⼀个重载运算符函数是成员函数,默认第一个是运算符传给隐式this指针,因此参数会比运算对象少一个。
- 运算符重载以后,其优先级和结合性与对应的内置类型运算符保持⼀致。
- 重载操作符必须有一个类类型参数
- . * :: sizeof ?:
- 重载++运算符是,前置++和后置++,运算符重载函数名都是operator++,为了区分,,后置++重载时,增加⼀个int形参。
- 重载<<和>>时,需要重载全局函数,因为重载为成员函数,第一位参数默认是this指针,第⼀个形参位置是左侧运算对象,调⽤时就变成了对象<<cout,不符合使⽤习惯和可读性。
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year =1, int month=1, int day=1)
{
_year = year;
_month = month;
_day = day;
}
bool operator==(Date d) //在类中
{
return _year == d._year &&
_month == d._month &&
_day == d._day;
}
Date& operator++(); //前置++
Date operator++(int); //后置++
int _year;
int _month;
int _day;
};
bool operator==(Date& d1, Date& d2) //在全局
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}
Date Date::operator++(int) //后置++
{
Date tmp = *this;
*this += 1;
return tmp;
}
Date& Date::operator++() //前置++
{
*this += 1;
return *this;
}
ostream& operator<<(ostream& out, const Date& d)
{
out << "年" << d._year << "月" << d._month << "日" << d._day << endl;
return out;
}
istream& operator >>(istream& in, Date& d)
{
while (1)
{
cout << "请依次输入年月日";
in >> d._year >> d._month >> d._day;
if (!d.CheckDate())
{
cout << "输入日期非法:";
d.Print();
cout << "请重新输入!!!" << endl;
}
else
{
break;
}
}
return in;
}
int main()
{
Date d1(2024, 7, 7);
Date d2(2024.7, 8);
if (d1.operator==(d2)) //声明在类中
operator==(d1, d2); //声明在全局中
if(d1 == d2) // 类/全局
{
printf("日期相同");
}
else
{
printf("日期不同");
}
return 0;
}
赋值运算符重载
赋值运算符重载是⼀个默认成员函数,⽤于完成两个已经存在的对象直接的拷⻉赋值,这⾥要注意跟 拷⻉构造区分,拷⻉构造⽤于⼀个对象拷⻉初始化给另⼀个要创建的对象。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2025-04-18,如有侵权请联系 cloudcommunity@tencent 删除c++变量编译器对象函数C++: 类和对象(中)
类的默认成员函数
构造函数
定义
代码语言:javascript代码运行次数:0运行复制#include<iostream>
using namespace std;
class Date
{
public:
void Init(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.Init(2024, 7, 20);
d1.Print();
return 0;
}
Date类型实例化时,每次都要调用Init进行初始化,这未免有点麻烦,C++则有构造函数,构造函数完美的替代了Init。
构造函数是一个特殊的成员函数,名字与类名相同, 创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。
特征
- 1. 函数名与类名相同。
- 2. ⽆返回值。(返回值啥都不需要给,也不需要写void,不要纠结,C++规定如此)
- 3. 对象实例化时系统会⾃动调⽤对应的构造函数。
- 4. 构造函数可以重载。
- 5. 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦用户显示式定义编译器将不再⽣成。
分类
无参构造函数
注意
代码语言:javascript代码运行次数:0运行复制#include<iostream>
using namespace std;
class Date
{
public:
// 1.⽆参构造函数
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;//正确的
Date d2();//错误的
d1.Print();
return 0;
}
要调用无参构造函数的时候,那么写 Date d2()编译器会有警告,不知道这是函数还是构造,使用这个写法是错误的。
带参构造函数
代码语言:javascript代码运行次数:0运行复制#include<iostream>
using namespace std;
class Date
{
public:
// 2.带参构造函数
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(2024,7,20);
d1.Print();
return 0;
}
全缺省构造函数
代码语言:javascript代码运行次数:0运行复制#include<iostream>
using namespace std;
class Date
{
public:
// 3.全缺省构造函数
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;
d1.Print();
return 0;
}
注意:无参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数,都叫做默认构造函数。 但是这三个函数有且只有⼀个存在,不能同时存在。
无参构造函数和全参构造函数虽然构成重载函数,但是调用时会发生歧义
不写时,编译器默认生成0的
对内置类型成员变量,是否初始化是不确定的,取决于编译器
对自定义类型成员变量,要求调用这个成员变量的默默人构造函数初始化。没有默认构造函就会报错,要初始化这个成员变量,需要用到初始化列表
初始化列表
除了以上的构造函数,在初始化成员变量主要使⽤函数体内赋值,构造函数还有另一种方式,就是初始化列表。结构如下
代码语言:javascript代码运行次数:0运行复制ClassName::ClassName(Type1 arg1, Type2 arg2)
:member1(arg1)
, member2(arg2)
{
// 构造函数体
}
- 每个成员变量在初始化列表中只能出现⼀次,语法理解上初始化列表可以认为是每个成员变量定义 初始化的地⽅。
- 引⽤成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始 化,否则会编译报错。
- C++11⽀持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显⽰在初始化列表初始化的 成员使⽤的。
- 优先使用初始化列表:尽量通过初始化列表显式初始化成员变量,这样可以避免未初始化的风险并提高代码的明确性。
- 内置类型的初始化问题:内置类型成员变量如果没有初始化,将导致不确定行为。因此,尽量避免依赖编译器的默认行为,明确初始化。
- 自定义类型成员的初始化要求:如果成员是自定义类型并且没有默认构造函数,在没有在初始化列表中进行初始化时,会导致编译错误。
- 初始化列表中是按照成员变量在类中的声明顺序进行初始化的,跟成员变量在初始化列表的顺序无关。
举例:
代码语言:javascript代码运行次数:0运行复制class Date
{
Date(int year = 1,int month = 1,int day = 1)
:_year(year)
,_month(month)
,_day(day)
{}
private:
int _year;
int _month;
int _day;
};
析构函数
定义
构造函数和析构函数有具有相反的作用。
析构函数:析构函数是一个特殊的函数,当对象被销毁时自动调用。它的名称与类名相同,但前面有一个波浪号(~),析构函数用于清理对象占用的资源。
代码语言:javascript代码运行次数:0运行复制class Date
{
public:
Date(int year,int month, int day)
{
cout << " Date(int year, int month, int day) " << endl;
_year = year;
_month = month;
_day = day;
}
~Date()
{
cout << " ~Date()" << endl;
}
private:
int _year;
int _month;
int _day;;
};
int main()
{
Date d1(2024,7,7);
return 0;
}
特征
1. 析构函数名是在类名前加上字符~。
2. ⽆参数⽆返回值。(这⾥跟构造类似,也不需要加void)
3. ⼀个类只能有⼀个析构函数。若未显式
代码语言:javascript代码运行次数:0运行复制#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;
}
private:
int _year;
int _month;
int _day;
};
定义,系统会⾃动⽣成默认的析构函数。
4. 对象⽣命周期结束时,系统会⾃动调⽤析构函数。
5. 跟构造函数类似,我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理,⾃定类型成员会 调⽤他的析构函数。
注意:对于⾃定义类型成员也会调⽤他的析构,也就是说⾃定义类型成员⽆论什么情况都会⾃动调⽤析构函数。
如果类中没有申请资源时,析构函数可以不写,直接使⽤编译器⽣成的默认析构函数,如Date;(上面的代码只是作为例子,~Date可以不写,编译器会自动生成)
拷贝构造函数
定义
拷贝构造函数:拷贝构造函数是一个特殊的构造函数,用于创建一个新对象,其内容完全相同于另一个已存在的对象。
特征
1、拷贝构造函数是构造函数的一个重载
2、拷贝构造函数的第一个参数必须是类类型对象的引用。
3、C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,所以这⾥⾃定义类型传值传参和传值返 回都会调⽤拷⻉构造完成。
代码语言:javascript代码运行次数:0运行复制#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) //error 引发无穷递归
Date(const Date& d) //拷贝构造函数
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024,10,10);
Date d2(d1); //拷贝构造
return 0;
}
若未显式定义拷⻉构造,编译器会⽣成⾃动⽣成拷⻉构造函数。⾃动⽣成的拷⻉构造对内置类型成 员变量会完成值拷⻉/浅拷⻉
赋值运算符重载
运算符重载
C++预定义的运算符,入“+""-"等,其操作对象只能是基本数据类型。在表达式中看到 “*” “+”时,C++对给定数据结构进行相乘,相加的运算,然后得出结果,当然,使用函数也是可以,类也能进行这样的操作,但语法就复杂多了。
那么,就可以多已有的运算符赋育新的含义,化繁为简,利用运算符来操作对象。
运算符重载是具有特殊函数名的函数 ,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名: 返回类型 operator 运算符 (参数类
型1 参数名1, 参数类型2 参数名2)
注意:
- 不能通过连接语法中没有的符号来创建新的操作符:⽐如operator@。
- 重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。一元运算符有一个参数,二元运算符有两个参数(左侧运算对象传给第一个参数,右侧传给第二个参数)。
- 如果⼀个重载运算符函数是成员函数,默认第一个是运算符传给隐式this指针,因此参数会比运算对象少一个。
- 运算符重载以后,其优先级和结合性与对应的内置类型运算符保持⼀致。
- 重载操作符必须有一个类类型参数
- . * :: sizeof ?:
- 重载++运算符是,前置++和后置++,运算符重载函数名都是operator++,为了区分,,后置++重载时,增加⼀个int形参。
- 重载<<和>>时,需要重载全局函数,因为重载为成员函数,第一位参数默认是this指针,第⼀个形参位置是左侧运算对象,调⽤时就变成了对象<<cout,不符合使⽤习惯和可读性。
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year =1, int month=1, int day=1)
{
_year = year;
_month = month;
_day = day;
}
bool operator==(Date d) //在类中
{
return _year == d._year &&
_month == d._month &&
_day == d._day;
}
Date& operator++(); //前置++
Date operator++(int); //后置++
int _year;
int _month;
int _day;
};
bool operator==(Date& d1, Date& d2) //在全局
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}
Date Date::operator++(int) //后置++
{
Date tmp = *this;
*this += 1;
return tmp;
}
Date& Date::operator++() //前置++
{
*this += 1;
return *this;
}
ostream& operator<<(ostream& out, const Date& d)
{
out << "年" << d._year << "月" << d._month << "日" << d._day << endl;
return out;
}
istream& operator >>(istream& in, Date& d)
{
while (1)
{
cout << "请依次输入年月日";
in >> d._year >> d._month >> d._day;
if (!d.CheckDate())
{
cout << "输入日期非法:";
d.Print();
cout << "请重新输入!!!" << endl;
}
else
{
break;
}
}
return in;
}
int main()
{
Date d1(2024, 7, 7);
Date d2(2024.7, 8);
if (d1.operator==(d2)) //声明在类中
operator==(d1, d2); //声明在全局中
if(d1 == d2) // 类/全局
{
printf("日期相同");
}
else
{
printf("日期不同");
}
return 0;
}
赋值运算符重载
赋值运算符重载是⼀个默认成员函数,⽤于完成两个已经存在的对象直接的拷⻉赋值,这⾥要注意跟 拷⻉构造区分,拷⻉构造⽤于⼀个对象拷⻉初始化给另⼀个要创建的对象。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2025-04-18,如有侵权请联系 cloudcommunity@tencent 删除c++变量编译器对象函数本文标签: C 类和对象(中)
版权声明:本文标题:C++: 类和对象(中) 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/jiaocheng/1747616635a2193841.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论