资讯动态

类和对象中

发布时间:2026/10/16 2:55:51 来源:尧图企业网站定制
目录1. 类的默认成员函数2. 构造函数3. 析构函数4. 拷贝构造函数5. 赋值运算符重载5.1 运算符重载5.2 赋值运算符重载5.3 日期类实现6. 取地址运算符重载6.1 const成员函数6.2 取地址运算符重载1. 类的默认成员函数•默认成员函数类中没有显示实现编译器自动生成的函数叫做默认成员函数。默认成员函数包含构造函数析构函数拷贝构造函数赋值重载函数普通对象取地址运算符重载函数const对象取地址运算符重载函数。2. 构造函数•构造函数是特殊的成员函数主要任务是在对象实例化时对它进行初始化构造函数会被自动调用。构造函数特点•构造函数函数名与类名相同函数无返回值对象实例化会自动调用对应构造函数构造函数可以重载。•类中没有显示实现构造函数包含构造函数和拷贝构造函数时编译器才会自动生成一个无参的默认构造函数。编译器自动生成的构造函数对内置类型int/char/double/指针等成员变量初始化没有要求是否初始化由编译器决定。对于自定义类型用class/struct等关键字自己定义的类型成员变量编译器会调用它的默认构造函数进行初始化没有默认构造函数则会报错。#includeiostream using namespace std; class Date { public: //不写构造编译器生成默认构造 void Print() { cout this-_year this-_month this-_day endl; } private: int _year; int _month; int _day; }; int main() { Date d1; d1.Print(); return 0; }vs编译器下自动生成的默认构造函数将成员变量初始化为随机值。•无参构造函数、全缺省构造函数与编译器自动生成的构造函数都叫做默认构造函数。但是这三个函数在一个类中只有一个存在不能同时存在无参构造函数与全缺省构造函数虽然构成重载但是在函数调用时会存在歧义。即可以不传实参就可以调用的构造函数就是默认构造函数。#includeiostream using namespace std; class Date { public: // 1.⽆参构造函数 Date() { _year 1; _month 1; _day 1; cout 无参构造函数 endl; } // 2.带参构造函数 不是默认构造函数 Date(int year, int month, int day) { _year year; _month month; _day day; cout 带参构造函数 endl; } // 3.全缺省构造函数 这里全缺省函数与带参构造函数形参类型/数量/顺序均相同不构成重载函数 /*Date(int year 1, int month 1, int day 1) { _year year; _month month; _day day; cout 全缺省构造函数 endl; }*/ void Print() { cout _year / _month / _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1; // 调⽤默认构造函数 Date d2(2025, 1, 1); // 调⽤带参的构造函数 // 注意如果通过⽆参构造函数创建对象时对象后⾯不⽤跟括号否则编译器⽆法 // 区分这⾥是函数声明还是实例化对象 Date d3();//C中 类型 对象() 会被识别为函数声明 d1.Print(); d2.Print(); return 0; }3. 析构函数析构函数在对象被销毁时完成资源的清理释放C规定规定对象在销毁时自动调用析构函数。析构函数特点•析构函数函数名是类名前加上“~”没有参数和返回值不需要加void,对象生命周期结束会自动调用析构函数。•类中没有显示实现析构函数时编译器会自动生成默认的析构函数。编译器自动生成的默认析构函数对内置类型成员不做处理自定义类型成员会调用它的析构函数。•类中没有申请资源或默认生成的析构函数可以使用时可以不写析构函数使用编译器自动生成的默认析构函数类中有资源申请需要自己写析构函数防止出现资源泄露。•C规定一个局部域中的多个对象后定义的先析构。#includeiostream 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; }; int main() { Stack s; return 0; } //局部对象s在main函数结束自动调用析构函数自己实现的析构函数会free掉malloc出来的资源 //不显示写析构函数时编译器调用默认生成析构函数仅会完成对象的销毁不会对malloc出来的资源释放引发内存泄漏4. 拷贝构造函数•拷贝构造函数是一个特殊的构造函数。拷贝构造函数特点•拷贝构造函数是构造函数的重载。•拷贝构造函数的第一个参数必须是类类型的对象的引用多个参数时第一个参数必须是类类型的对象的引用其他参数都需要缺省值。#includeiostream 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; } //带参构造函数参数为Date*类型 Date(Date* d) { _year d-_year; _month d-_month; _day d-_day; } private: int _year; int _month; int _day; }; void func(Date d) { } int main() { Date d1(2005, 10, 5);//调用默认构造函数 Date d2(d1);//调用带参构造函数 Date d3 d1;//调用带参构造函数 Date d4(d1);//调用拷贝构造函数 Date d5 d1;//调用拷贝构造函数 func(d1);//函数传参调用拷贝构造 return 0; }注意拷贝构造函数的第一个参数必须是类类型的对象的引用如果不是调用拷贝构造函数时先进行函数传参函数传参会调用拷贝构造函数调用拷贝构造函数又会先传参传参过程又调用拷贝构造函数……最终会无穷调用拷贝构造函数加引用后就解决了这个问题。•C规定自定义类型对象进行拷贝行为时需要调用拷贝构造自定义类型传值传参和传值返回都会调用拷贝构造。•若未显式定义拷贝构造函数编译器会自动生成拷贝构造函数自动生成的拷贝构造函数对内置类型完成浅拷贝按字节直接复制成员的值自定义类型需要调用它的拷贝构造。•类中如果申请了资源就需要自己显示实现拷贝构造函数同时也需要显示实现析构函数释放资源。#includeiostream using namespace std; typedef int STdatetype; class Stack { public: //构造函数 Stack(int n 4) { _a (STdatetype*)malloc(sizeof(STdatetype) * n); if (_a nullptr) { perror(malloc申请空间失败); return; } capacity n; top 0; } //拷贝构造函数 Stack(const Stack st) { _a (STdatetype*)malloc(sizeof(STdatetype) * st.capacity); if (_a nullptr) { perror(malloc申请空间失败); return; } memcpy(_a,st._a,sizeof(STdatetype)*st.top); capacity st.capacity; top st.top; } //插入元素 void Push(STdatetype s) { //扩容 if (top capacity) { size_t newcapacity capacity*2; STdatetype* tmp (STdatetype*)realloc(_a,sizeof(STdatetype) * newcapacity); if (tmp nullptr) { perror(realloc申请空间失败); return; } _a tmp; capacity newcapacity; } _a[top] s; } //析构函数 ~Stack() { cout ~Stack endl; free(_a); _a nullptr; top capacity 0; } private: STdatetype* _a; size_t capacity;//元素个数 size_t top;//插入元素位置 }; class Myqueen { public: private: Stack pushst; Stack popst; }; int main() { Stack st1;//调用构造函数 st1.Push(1); st1.Push(2); Stack st2 st1;//调用拷贝构造函数 //如果没有显示实现拷贝构造函数编译器自动生成的拷贝构造函数只会完成浅拷贝 //st2与st1指向同一块空间析构函数会对这块空间析构两次程序崩溃 Myqueen qu1; Myqueen qu2 qu1; // Myqueen⾃动⽣成的拷⻉构造会⾃动调⽤Stack拷⻉构造完成qu1,qu2 // 的拷⻉只要Stack拷⻉构造⾃⼰实现了深拷⻉就没有问题 return 0; }注意自己显示实现构造函数编译器会自动生成默认拷贝构造函数不会生成无参构造函数自己显示实现拷贝构造函数编译器不会自动生成无参构造函数与拷贝构造函数•传值返回会产生临时对象调用拷贝构造使用传引用返回返回的是返回对象的引用不会产生临时对象没有拷贝可以提高效率。注意:如果返回对象是局部域的局部对象函数结束就销毁了此时传引用返回指向已销毁的对象属于未定义行为bug需避免这种使用。5. 赋值运算符重载5.1 运算符重载C自定义类型对象使用运算符时必须转换成调用对应运算符重载若没有对应的运算符重载则会编译报错。因此当自定义类型对象使用运算符时需要对运算符进行重载赋予它新的含义。运算符重载特点•运算符重载函数是具有特殊名字的函数结构与函数相似名字是operator和你需要定义的运算符共同构成。•运算符重载函数的参数个数与运算符作用的对象个数一致一元运算符有一个参数二元运算符有两个参数左侧运算符对象传给第一个参数右侧运算符对象传给第二个参数。•若运算符重载函数为成员函数则运算符的第一个对象默认传给隐式this指针所以当运算符重载函数作为成员函数时函数参数比运算对象少一个。•语法中没有的符号无法进行重载如 .* :: sizeof 无法进行运算符重载。重载操作符必须有一个类类型参数不能通过运算符重载改变类类型对象含义。非法重载#includeiostream using namespace std; class A { int x; }; //int operator (int x, int y) //非法重载参数必须要有一个自定义类型 //{ // return x y; //} int main() { return 0; }•重载运算符时分为前置和后置运算符重载函数名都是operator,无法进行区分所以后置重载时增加一个int形参与前置构成重载。重载#includeiostream using namespace std; class Date { public: Date(int year 1, int month 1, int day 1) { _year year; _month month; _day day; } void Print() { cout _year - _month - _dayendl; } bool operator( const Date d1) { return _year d1._year _month d1._month _day d1._day; } Date operator()//前置返回实现功能后的自己可以传引用返回 { //先再返回 _year; _month; _day; return *this; } Date operator(int)//后置返回实现功能前的自己也就是tmp,这里tmp是局部变量无法传引用返回 { Date tmp *this; //先返回再 _year; _month; _day; return tmp; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 3, 5); Date d2(2026, 3, 6); Date d3(2026, 3, 5); cout d1.operator( d2) endl;//operator(d1, d2)运算符重载函数可以显示调用 cout (d1 d3) endl;//d1 d3 ,编译器会转换为operatord1,d3 Date d4 d1; d4.Print(); Date d5 d3; d5.Print(); return 0; }前置与后置都会使对象本身区别在返回值不同前置返回后的对象后置返回前的对象。•重载与时需要重载为全局函数因为重载为成员函数时this指针会默认抢占第一个形参位置与运算符左侧对象对应实际运算符左侧对象为ostream/istream,与使用习惯不符。成员函数指针使用#includeiostream using namespace std; class A { public: void func() { cout func() endl; } }; typedef void (A:: *FF)();//FF为指向A类域中、返回值为void 、无参成员函数的指针类型 int main() { FF pf A::func;//成员函数名必须加取到函数指针pf为一个成员函数指针指向func函数 A aa; (aa.*pf)();//对象调用成员函数使用.*运算符 return 0; }运算符重载#includeiostream using namespace std; class Date { public: Date(int year 1, int month 1, int day 1) { _year year; _month month; _day day; } void Print() { cout _year - _month - _dayendl; } //private: int _year; int _month; int _day; }; bool operator(const Date d1, const Date d2) { return d1._year d2._year d1._month d2._month d1._day d2._day; } int main() { Date d1(2026, 3, 5); Date d2(2026, 3, 6); Date d3(2026, 3, 5); cout operator(d1, d2) endl;//operator(d1, d2)运算符重载函数可以显示调用 cout (d1 d3) endl;//d1 d3 ,编译器会转换为operatord1,d3 return 0; }重载为全局的面临对象访问私有变量时的方法1.成员放公有2.通过成员函数简接访问3.友元函数4.将该函数重载为全局函数。5.2 赋值运算符重载赋值运算符重载函数是一个默认成员函数用于完成两个已经存在的对象的直接拷贝赋值。与拷贝构造区分拷贝构造用于完成一个已存在对象拷贝初始化给一个要创建的对象。赋值运算符重载特点•赋值运算符需要重载为成员函数参数可以写成const当前类类型引用返回值可以写成类类型引用减少传值传参的拷贝同时支持连续赋值的场景。•没有显示实现时编译器会生成默认运算符重载函数默认运算符重载函数对内置类型只进行浅拷贝对自定义类型成员变量会调用它的运算符重载函数。赋值运算符重载#includeiostream 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; } Date operator(const Date d) { if (this ! d) { _year d._year; _month d._month; _day d._day; } return *this; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 9, 4);//构造 Date d2(2026,9,5);//构造 Date d3(d1);//拷贝构造 d3.Print(); d1 d2;//赋值 d1.Print(); Date d4 d1;//拷贝构造 d4.Print(); return 0; }拷贝构造用于一个已存在对象拷贝初始化给另一个要创建对象赋值重载完成两个已存在对象直接的拷贝赋值。5.3 日期类实现Date.h文件#pragma once #includeiostream #includeassert.h using namespace std; class Date { public: //友元函数声明 friend ostream operator(ostream out,const Date d); friend istream operator(istream in, Date d); int GetMonthDay(int year, int month) const { assert(month 1 month 12); //将数组MonthArr设为静态数组可以避免每次函数调用都重新生成一个数组提高效率 static int MonthArr[13] { -1,31,28,31,30,31,30,31,31,30,31,30,31 }; if (month 2 ((year % 4 0 year % 100 ! 0) || (year % 400 0))) return 29; else return MonthArr[month]; } //只要不改变调用对象本身的函数都可以加const void Print() const; bool CheckDate() const; Date(int year 1, int month 1, int day 1); Date operator (int day); Date operator (int day) const; Date operator -(int day); Date operator -(int day) const; Date operator (); Date operator (int); Date operator --(); Date operator --(int); bool operator(const Date d) const; bool operator!(const Date d) const; bool operator(const Date d) const; bool operator(const Date d) const; bool operator(const Date d) const; bool operator(const Date d) const; int operator -(const Date d) const; private: int _year; int _month; int _day; }; ostream operator(ostream out, const Date d); istream operator(istream in, Date d);Date.cpp文件#define _CRT_SECURE_NO_WARNINGS #includeDate.h void Date::Print() const { cout _year _month - _day endl; } bool Date::CheckDate() const { if ((_month 1 _month 12) (_day 1 _day Date::GetMonthDay(_year,_month))) return true; else return false; } Date::Date(int year , int month , int day ) { _year year; _month month; _day day; if (!CheckDate()) { cout 非法日期- *this; } } Date Date::operator(int day) { if (day 0) return *this - -day; _day day; while (_day GetMonthDay(_year, _month)) { _day - GetMonthDay(_year, _month); _month; if (_month 13) { _month 1; _year; } } return *this; } Date Date::operator (int day) const { Date tmp(*this); tmp day; return tmp; } Date Date::operator -(int day) { if (day 0) return *this -day; _day - day; while (_day 0) { //需要先对month进行处理方便后面向前一月借天数 --_month; if (_month 0) { _month 12; _year--; } _day GetMonthDay(_year, _month); } return *this; } Date Date::operator -(int day) const { Date tmp(*this); tmp - day; return tmp; } Date Date::operator () { *this 1; return *this; } Date Date::operator (int) { Date tmp(*this); *this 1; return tmp; } Date Date::operator --() { *this - 1; return *this; } Date Date::operator --(int) { Date tmp(*this); *this - 1; return tmp; } bool Date::operator(const Date d) const { return _year d._year _month d._month _day d._day; } bool Date::operator!(const Date d) const { return !(*this d); } bool Date::operator(const Date d) const { if (_year d._year) return true; else if (_year d._year _month d._month) return true; else if (_year d._year _month d._month _day d._day) return true; return false; } bool Date::operator(const Date d) const { return *this d || *this d; } bool Date::operator(const Date d) const { return !(*this d); } bool Date::operator(const Date d) const { return !(*this d); } int Date::operator -(const Date d) const { Date max *this; Date min d; int flag 1; if (*this d) { max d; min *this; flag -1; } int n 0; while (min ! max) { min; n; } return n * flag; } 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 请输入年月日 endl; in d._year d._month d._day; if (d.CheckDate()) { break; } cout 非法日期重新输入 endl; } return in; }Test.cpp文件#define _CRT_SECURE_NO_WARNINGS #includeDate.h #includeiostream using namespace std; void TestDate1() { Date d1(2025, 4, 24); //Date d2(d1 100); Date d2 d1 100; d1.Print(); d2.Print(); Date d3 d1 100; d1.Print(); d3.Print(); } void TestDate2() { Date d1(2025, 4, 26); Date d2 d1 - 10000; d1.Print(); d2.Print(); Date d3(2025, 4, 26); Date d4 d3 - 10000; d3.Print(); d4.Print(); Date d5(2025, 4, 26); d5 -100; d5.Print(); } void TestDate3() { Date d1(2025, 4, 26); Date d2 d1; //Date d2 d1.operator(0); d1.Print(); d2.Print(); Date d3(2025, 4, 26); Date d4 d3; d3.Print(); d4.Print(); } void TestDate4() { Date d1(2025, 4, 26); Date d2(2025, 7, 1); cout (d1 d2) endl; cout (d2 d1) endl; cout (d1 d2) endl; cout d1 - d2 endl; Date d3(2005, 4, 26); Date d4(2100, 1, 1); cout d3 - d4 endl; } void TestDate5() { Date d1(2100, 2, 29); Date d2(2000, 2, 29); // 流插入 cout d1 d2; //operator(cout, d1); cin d1 d2; cout d1 d2; cout d1 - d2 endl; } void TestDate6() { Date d1(2100, 2, 28); d1.Print(); d1 100; d1 100; const Date d2(2100, 2, 28); d2.Print(); d2 100; //d2 100; d2为const Date类型不支持 cout d1 endl; cout d2 endl; } int main() { TestDate1(); cout endl; TestDate2(); cout endl; TestDate3(); cout endl; TestDate4(); cout endl; TestDate5(); cout endl; TestDate6(); cout endl; return 0; }6. 取地址运算符重载6.1 const成员函数将const修饰的成员函数称为const成员函数const修饰成员函数时放到函数参数列表后面。•普通成员函数this指针类型Date* const this,this指针不能指向其他对象但可以通过this指针修改对象的成员。•const实际修饰成员函数中隐含的this指针表明在该成语函数中对类的任何成员都不能进行修改。const修饰Date类的Print函数时隐含的this指针由Date* const this转变为const Date* const this无法再通过this指针修改对象的成员。权限可以缩小权限不能放大普通对象可以调用const成员函数和普通成员函数const对象不能调用普通成员函数只能调用const成员函数。#includeiostream using namespace std; class Date { public: Date(int year 1, int month 1, int day 1) { _year year; _month month; _day day; } // void Print(const Date* const this) const void Print() const { cout _year - _month - _day endl; } private: int _year; int _month; int _day; }; int main() { // 这⾥⾮const对象d1也可以调⽤const成员函数Print,是⼀种权限的缩⼩ Date d1(2026, 8, 5); d1.Print(); const Date d2(2026, 9, 5); d2.Print(); return 0; }6.2 取地址运算符重载取地址运算符重载分为普通运算符重载和const运算符重载。一般不需要显示实现编译器原生存在的取地址足够我们使用。自己显示实现可以按需求返回需要的地址class Date { public : Date* operator()//非const版本this为Date* const 类型 { return this; // return nullptr; } const Date* operator()const//const版本this为const Date* const 类型 { return this; // return nullptr; } private : int _year ; // 年 int _month ; // ⽉ int _day ; // ⽇ };

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑