目录
前言
探讨类的六个默认成员函数,这是C++中类的基础,需要对C语言有一定的基础。
提示:以下是本篇文章正文内容,下面案例可供参考
一、类
不同与C语言C++定义了类,类的关键字是class,结构体在C++中也是类,但一般使用class。下面是类的基本格式。不同与C语言,函数就是函数,无非是不是自定义的函数而已。在C++中,类里面函数被称之为成员函数。而里面的变量被称为成员变量。
在类(class)里面有三个访问限定符代表三种权限。public(公有)protected(保护)
private(私有)
class class_name
{
public:
//......
protected:
//......
private:
//......
};1:public修饰的成员在类外可以直接被访问。
2:protected和private修饰的成员在类外不能被直接访问。
3:访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止。
4:如果后面没有访问限定符,作用域就到 }; 为止代表类结束了。
5:若不写访问限定符,class的默认访问权限为private,struct的默认访问权限为public(C++兼容C)
6:访问限定符只在编译时有用。
//class class_name // class_name是类的名字 {}是类的主体,类主体结束的时候是有;的要主要
//{
// // 类体:由成员函数(类里面定义的函数)和成员变量(类里面定义的变量)组成
// // 类有两种定义方式:
// // 1:声明和定义全部放在类中定义,如果成员函数在类中定义,编译器可能会将其当作内联函数处理
// class wyb
// {
// public: // 成员函数
// void init(char* name, char* sex, int* age)
// {
// _name = name;
// _sex = sex;
// _age = age;
// }
// void show()
// {
// cout << _name << "-" << _sex << "-" << _age << endl;
// }
// public: // 成员变量(类声明)
// char* _name;
// char* _sex;
// int* _age;
// };
//};
二、类的默认成员函数
1.引入
一个类如果什么都没写简称空类,而当空类什么都没有的时候编译器会自动引入六个默认成员函数(程序员没有写这种成员函数会自动引入)。
class class_name
{
//......
};
2.构造函数
构造函数是一个特殊的成员函数,它的名字与类名相同,在创建对象时被调用,并且整个对象周期只调用一次,无返回值,无需写void,构造函数可以重载,对象实例化时编译器自动调用对应的构造函数,主要目的是为了初始化对象,并不开空间。
如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明。如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦程序中显示编译器将不会在生成构造函数。无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
class date
{
public:
// 无参构造函数
// date()
// {}
// 构造函数
date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
cout << " dete() " << endl;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
void inof()
{
date d1(2024, 7, 12);
}
void print_()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1(2024,7,10), d2(2024,7,11);
d1;
d1.print();
d1.inof();
d1.print_();
return 0;
}
3. 析构函数
析构函数的功能完成对象中资源的清理工作,它是在对象生命结束的时候会被自动调用。析构函数名是在类名前加上字符 ~。 无参数无返回值类型。 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。析构函数不能重载数,如果程序员显式定义析构函数,则C++编译器会自动生成一个无参的默认析构函数,一旦程序中显示编译器将不会在生成析构函数。
class date { public: date(int year, int month, int day) { _year = year; _month = month; _day = day; cout << " 调用构造函数 " << endl; } ~date() { cout << "对象声明周期结束自动调用析构函数" << endl; } void print() { cout << _year << "-" << _month << "-" << _day << endl; } void inof() { date d3(2024, 7, 12); d3.print_(); } void print_() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; int main() { date d1(2024, 7, 10), d2(2024, 7, 11); d1; d1.print(); d2.inof(); d2.print_(); return 0; }
在C++中函数遵循后进先出的顺序,最后一个函数会先被析构。这里创建的析构的顺序是d1->d2->d3,所以析构顺序是d3->d2->d1(局部对象的生命周期在它们所在的函数或块结束时终止,而析构函数则在此时被调用。在你的代码中,d3是在d2.inof函数中创建的局部对象,因此当inof函数结束时,d3的析构函数被调用。接着,当main函数结束时,d2和d1的生命周期也随之结束,它们的析构函数依次被调用)
4.拷贝构造函数
拷贝构造函数只有一个参数,并且拷贝构造函数是构造函数的重载,用同类型的对象进行构造。C++规定内置类型直接拷贝,自定义类型必须调用拷贝构造完成拷贝(重点)
class date
{
public:
date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
cout << " 调用构造函数 " << endl;
}
~date()
{
cout << "对象声明周期结束自动调用析构函数" << endl;
}
date(date& d1)
//date(const date& d)
// C++规定如果是传值传参必须调用拷贝构造,并且必须使用引用,没有引用会造成递归
{
_year = d1._year;
_month = d1._month;
_day = d1._day;
cout << " 调用拷贝构造函数 " << endl;
}
void print_()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1(2024, 7, 10);
date d2(d1);
d2.print_();
return 0;
}
如果程序员没有定义拷贝构造,编译器会生成默认的拷贝构造。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝(一个字节一个字节的拷贝),这种拷贝叫做浅拷贝,或者值拷贝。如果是想顺序表那样的动态拷贝,进行默认拷贝会崩溃(那栈举例子在默认拷贝的时候如果拷贝的函数里面有地址,拷贝的函数与被拷贝的函数会指向同一个地址,当对象结束,进行析构的时候会拷贝的函数被析构,被拷贝的函数也会被析构,这就造成同一个地址空间会析构两次就导致了程序崩溃)。所以在有动态内存的场景要使用自定义拷贝构造进行拷贝。
拷贝构造除去浅拷贝还有深拷贝,深拷贝比较复杂,后面会单独出一个深拷贝的文章。
class date
{
public:
date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
cout << " 调用构造函数 " << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1(2024, 7, 10);
date d2(d1);
return 0;
}
5.运算符重载
这里面有一些比较细节的, d1<d2(转换成operator<(d1,d2)) 实际上是调用bool operator<(const date& x1, const date& x2)这个函数,有一点要注意就是当,bool operator<(const date& x1, const date& x2)(类外)在对象里面,就会出现编译不过的问题,说是参数太多了,那是因为它支持重载,在里面有隐含的this指针,运算符重载(函数名是特殊的关键字(operator)后加需要重载的运算符符号,{ .* :: sizeof ?: . 以上5个运算符不能重载 },作为类成员重载时,其形参比操作符数目少一个(隐藏this),必须有一个类型参数,不能重载内置类型如bool operator<(const int& x1, const int& x2)如果在类里面,如d1 = d2的情况是这样定义的 bool operator<(const date& x)(类中) 其中隐藏一个this指针而已 (d1 < d2; 转换为d1.opreator<(d2)其中d1传给了隐藏的this,d2传给了x)
下面我们学赋值运算符 = 它与构造函数有所不同
存在的两个对象直接的复制拷贝 --赋值运算符重载 d1 = d2;
用一个以及存在的对象初始化另一个对象 --构造函数 date d3(d2); 赋值运算符只能重载成类的成员函数不能重载成全局函数 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝
class date
{
public:
date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
int _year;
int _month;
int _day;
// 对象里面也有两个不过是应为this指针是隐含的,所以只显示一个
date operator=(const date& x)
{
if (this != &x) // 防止自我赋值
{
_year = x._year;
_month = x._month;
_day = x._day;
}
return *this;
}
};
// 对象外面有两个
bool operator<(const date& x1, const date& x2)
{
if (x1._year < x2._year)
{
return true;
}
else if (x1._year == x2._year && x1._month < x2._month)
{
return true;
}
else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
{
return true;
}
return false;
}
int main()
{
date d1(2024, 7, 10), d2(2024, 7, 11);
d1 < d2; //转换成operator<(d1,d2)
// 以及存在的两个对象直接的复制拷贝 --赋值运算符重载
d1 = d2; //转换为d1.opreator=(d2)
cout << (d1 < d2) << endl;
// 用一个以及存在的对象初始化另一个对象 --构造函数
date d3(d2);
return 0;
}
运算符重载的语法大致相同,但是由于++的特殊性前置++,与后置++略有不同,前置++与其它运算符语法的写法相同,后置的参数哪里多了一个int,这个int不做任何改变,仅仅是为了区分而已。至于其他的运算符照猫画虎就可以写出来。
class date
{
public:
date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
cout << " dete() " << endl;
}
// 前置++(先++在赋值),this指向的对象函数结束后不会销毁,故用引用返回来提高效率
date& operator++()
{
_day += 1;
return *this;
}
// 后置++(先赋值在++) C++规定后置++重载时多增加了一个int类型的参数加这个int参数仅仅是为了区分
//
date operator++(int)
{
date temp(*this);
_day += 1;
return temp;
}
//private:
int _year;
int _month;
int _day;
};
int main()
{
date d1(2024,7,10);
++d1;
date d2(d1);
d1++;
cout << d1._day << endl;
cout << d2._day << endl;
return 0;
}
6.const成员函数
const成员函数格式:成员函数后加const 。const修饰成员函数修饰的不是this是*this
this本身的类型是date* const this,修饰的是this指针本身,this本身是不能改变的
const date* const this,修饰的是*this,*this的类型是不能够改变的。目的是:const对象可以调用成员函数,非const的对象也可以调用,权限是可以缩小的。权限可以缩小不能放大
1. const对象可以调用非const成员函数吗?2. 非const对象可以调用const成员函数吗?3. const成员函数内可以调用其它的非const成员函数吗?4. 非const成员函数内可以调用其它的const成员函数吗?
class date
{
public:
date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void prin()
{
cout << "printf()" << endl;
}
void prinf() const
{
cout << "printf()const" << endl;
}
int _year;
int _month;
int _day;
};
int main()
{
date d1(2024,7,10);
d1.prinf();
d1.prin();
const date d2(2024,7,11);
d2.prinf();
// d2.prin();
return 0;
}
7.取地址及const取地址操作符重载
取地址及const取地址操作符重载,这两个默认成员函数一般不需要重新定义,编译器默认会生成。平时不用写,只有一种情况需要写,就是不然别人取到对象可修改的地址。
class date
{
public:
date(int year = 2024, int month = 7, int day = 11)
{
_year = year;
_month = month;
_day = day;
}
// 普通对象取地址
date* operator&()
{
cout << "date(d) & " << endl;
// return this;
return nullprt
}
// const对象取地址
const date* operator&()const
{
cout << "cont date(c) &" << endl;
return this;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
date d1, d2;
const date c1, c2;
cout << &d1 << endl;
cout << &d2 << endl;
cout << &c1 << endl;
cout << &c2 << endl;
return 0;
}