当前位置: 首页 > news >正文

无锡网站托管网站收录不好的原因

无锡网站托管,网站收录不好的原因,嵩县网站开发,电子商城网站怎么做【C初阶】之类和对象#xff08;中#xff09; ✍ 类的六个默认成员函数✍ 构造函数#x1f3c4; 为什么需要构造函数#x1f3c4; 默认构造函数#x1f3c4; 为什么编译器能自动调用默认构造函数#x1f3c4; 自己写的构造函数#x1f3c4; 构造函数的特性 ✍ 拷贝构造… 【C初阶】之类和对象中 ✍ 类的六个默认成员函数✍ 构造函数 为什么需要构造函数 默认构造函数 为什么编译器能自动调用默认构造函数 自己写的构造函数 构造函数的特性 ✍ 拷贝构造函数 编译器默认生成的拷贝构造函数 自己写的拷贝构造函数 拷贝构造函数调用的场景 ✍ 赋值运算符重载也叫拷贝赋值函数 运算符重载的引入 前置和后置重载 运算符重载函数的调用 赋值运算符重载 编译器默认生成的赋值运算符重载函数 自己显示写的赋值运算符重载函数 ✍ 析构函数 编译器默认生成的析构函数 显式写的析构函数 析构函数的特性 没有深拷贝导致二次释放同一空间问题 问题的引入---拷贝构造函数 问题的解决---深拷贝 赋值运算符重载函数的浅拷贝问题 ✍ const成员函数 const对象访问的规则 非const对象访问的规则 ✍ 对普通对象的取地址运算符重载和对const对象取地址运算符重载✍ C默认构造函数提供的机制 C默认构造函数是否提供的情况 博客主页 小镇敲码人 热门专栏C初阶 欢迎关注点赞 留言 收藏 任尔江湖满血骨我自踏雪寻梅香。 万千浮云遮碧月独傲天下百坚强。 男儿应有龙腾志盖世一意转洪荒。 莫使此生无痕度终归人间一捧黄。 ❤️ 什么你问我答案少年你看下一个十年又来了 ✍ 类的六个默认成员函数 当类为空是编译器也不是什么都不生成而是会生成六大默认成员函数。 我们也可以自己显式把这六个默认成员写出来这样编译器就会调用我们自己的而不会调用默认生成的。 ✍ 构造函数 为什么需要构造函数 我们学习C语言的时候初始化栈操作需要自己写一个Init函数但是这样就很麻烦因为初始化栈之后需要我们显示的去调用Init函数否则就有可能出现野指针的情况因为如果是链式的栈要把next指针初始化为空。 默认构造函数 我们构造函数就是为了解决这样的问题在初始化类的时候 你不需要显示的调用Init函数编译器会自动的去调用如果你不去显示的写 编译器会生成一个默认的构造函数。我们来验证一下。class Date { private:int year;int day;int month; }; int main() {Date x;return 0; }此时我们写了一个Date类编译器会给调用它的默认构造函数吗运行结果 怎么回事呢x对象的值没有被初始化呀那是不是代表编译器没有调用默认构造函数呢其实不然C把类型分为自定义类型和内置类型默认构造函数要做的是自定义类型去调用它自己的构造函数如果有的话内置类型去给一个随机值那到底是不是这样呢?我们也可以来验证一下。 class year { public:year(){std::cout year() std::endl;} }; class Date { private:year y;int day;int month; }; int main() {Date x;return 0; }注意那个自定义类型的构造函数必须是public的否则在它自己的类外面就访问不了。 运行结果 默认构造函数默认成员函数是两个不同的概念两者不能混淆不用我们传参数全缺省构造函数和无参数构造函数、默认生成的构造函数都称作为默认构造函数。 class Date { public:Date(){day 0;month 0;}Date(int day 0,int month 0){} private:year y;int day;int month; }; int main() {Date x;return 0; }注意上面那两个默认构造函数不能同时存在因为都不需要传参数会造成歧义编译器不知道调用哪一个默认构造函数。 为什么编译器能自动调用默认构造函数 那为什么编译器能在实例化类对象的的时候自动调用它的构造函数呢 可以认为这是编译器做了特殊的处理它帮助我们调用了这个函数。我们转到反汇编可以发现编译器帮助我们调用了。 自己写的构造函数 我们也可以自己显示的写构造函数那样编译器就不会去调用默认生成的构造函数了。 class year { public:year(){std::cout year() std::endl;} }; class Date { public:Date(){day 0;month 0;} private:year y;int day;int month; }; int main() {Date x;return 0; }运行结果 可以看到编译器在我们自己写的构造函数进去前仍然会先去调用自定义类型的构造函数。 构造函数的特性 1、一次实例化对象只会调用一次不支持显示调用。 2、构造函数会在实例化对象的时候自动调用只用于初始化对象的一些成员变量是初始化对象而不是给对象开空间。 3、函数名和类名相同无返回值。 4、支持重载。 ✍ 拷贝构造函数 拷贝构造函数是构造函数的一种主要作用是实现用一个已经存在的类对象去初始化创建另外一个类对象。 编译器默认生成的拷贝构造函数 和前面普通的构造函数一样如果我们不写编译器就会默认生成。 class year { public:year(){std::cout year() std::endl;} }; class Date { public:Date(){day;month;}void f(){} private:year y;int day 0;int month 0; }; int main() {Date x;Date y(x);return 0; }运行结果 那我们为什么要写呢这样岂不是浪费时间多次一举吗编译器都帮助我们写好了我们有时候的确是不需要写的比如在没有向堆申请空间的时候这时不涉及资源的清理浅拷贝不会出问题但是一旦我们向堆上申请空间后不自己写深拷贝的拷贝构造函数就会造成二次释放相同空间的问题。 自己写的拷贝构造函数 Date(const Date x) {day x.day;month x.month;y x.y; }这里加上const是因为我们只是用x去初始化但不希望改变它的值至于这里为什么要使用引用而且必须使用引用否则就会引发无穷递归 这是因为我们在传参的时候实参和形参的关系是形参是实参的拷贝当两者类型一样时这不就相当于使用实参去初始化形参吗也就是一个类去初始化另外一个类也要调用拷贝构造函数下一次又是一样的情况所以会造成无穷递归但是加了引用你这个形参就是我实参的别名不用再去调用拷贝构造也就不会出现这种问题。 拷贝构造函数调用的场景 刚刚我们其实已经说了两个场景了。 1、用一个创建好的类初始化另外一个没有初始化的类 2、函数传参参数为自定义类型 3、函数返回值参数为自定义类型2和3都不能带引用否则就不会调用拷贝构造函数。 4、赋值运算符重载时被赋值的类还没有创建。 ✍ 赋值运算符重载也叫拷贝赋值函数 我们的内置类型可以支持一个变量赋值给另外一个对象比如a b(都是int类型)那我们类自定义类型支持吗答案是肯定的使用运算符重载就可以解决这个问题。 运算符重载的引入 在C中增加了运算符的重载这是因为有时候自定义类型也需要做一些类似操作符的操作引入运算符重载极大的提升了代码的可读性它的规则如下 函数名为operator后面接需要重载的运算符注意不能重载一些莫名奇妙的符号像。函数原型返回值类型 operator操作符(参数列表) 注意运算符重载时必须要有一个自定义类型的参数因为运算符重载就是为类而生的如果你没有类参数那就没有意义了。 编译器为了防止你乱搞会报错的。上面是全局的运算符重载函数。 有时候有的运算符需要两个参数但是我们在类里面设计的时候只有一个参数实际上是有两个参数的第一个参数传的是this指针编译器给隐藏了 前置和后置重载 我们来介绍一下两个特殊的运算符重载前置和后置重载这两个操作符名字都一样该如何区分呢 这里没有办法了C对其做了特殊的处理即给后置多传一个参数来去区分并且操作符重载最多额外传一个int参数作区分也是为了防止用户乱搞。 我们来实现一下Date类的前置和后置 // 前置运算符 // 该运算符将对象的年份、月份和日期都递增1并返回递增后的对象的引用 Date operator() // 前置 { year; // 递增年份 day; // 递增日期 month; // 递增月份 return *this; // 返回当前对象的引用 } Date operator(int) // 后置 { Date tmp(*this); // 创建当前对象的副本 (*this); // 递增当前对象使用前置 return tmp; // 返回递增前的对象的副本 }这里实际上我们在日期的时候要考虑月份和年份的变化这里我们主要是学习语法就不考虑了。 注意后置的返回值不能带引用。因为我们返回的是副本但是副本是临时对象出了作用域销毁了所以我们需要返回一个副本的拷贝而不是副本本身。 注意这里即使我们运算符重载函数写成全局的也能像内置类型那样调用 using namespace std; class Date { public:Date(int year, int month 2, int day 1) ://普通的构造函数year_(year),month_(month),day_(day){cout Date(int year, int month 2, int day 1) endl;}Date(const Date x) ://拷贝构造函数year_(x.year_),month_(x.month_),day_(x.day_){cout Date(const Date x) endl;}Date operator(const Date x)//拷贝赋值函数{year_ x.year_;month_ x.month_;day_ x.day_;cout operator(const Date x) endl;return *this;}~Date()//析构函数{cout ~Date endl;} public:int year_;int month_;int day_; };// 前置运算符 // 该运算符将对象的年份、月份和日期都递增1并返回递增后的对象的引用 Date operator(Date x) // 前置 {x.year_; // 递增年份 x.day_; // 递增日期 x.month_; // 递增月份 return x; // 返回当前对象的引用 }Date operator(Datex,int) // 后置 {Date tmp(x); // 创建当前对象的副本 x; // 递增当前对象使用前置 return tmp; // 返回递增前的对象的副本 }int main() {Date x(2022);x;x; }运行结果 代码正常运行。 如果我们给运算符重载函数增加其它类型的参数编译器就会报错 运算符重载函数的调用 内置类型可以直接a b,或者a那我们的自定义类型是否可以这样了为了可读性和方便我们的C支持这样来调用运算符重载函数我们拿刚刚的前置、和后置函数来演示。 int main() {Date x;x;//--operator(x,1);x;//--operator(x);return 0; }我们转到反汇编可以发现确实是调用了对应的函数。 也可以显示调用注意这里编译器已经帮助我们传了this指针过去所以这里我们显示调用的是后置 赋值运算符重载 回归正题我们继续来看我们的赋值运算符重载函数。 编译器默认生成的赋值运算符重载函数 当我们不去显示的写赋值运算符重载函数编译器会默认生成一个。 但是当我们这样去写被赋值的y还没有被创建这个时候编译器就会去调用拷贝构造函数无论你有没有自己显式的写 自己显示写的赋值运算符重载函数 下面我们来自己显示的写一下还是会有深拷贝的问题当我们类的成员变量有在堆上申请空间时直接赋值会引发二次析构的问题。 // 赋值运算符重载函数 // 将参数x的值赋给当前对象并返回当前对象的引用 Date operator(const Date x) { if (this ! x) // 检查自赋值 { day x.day; month x.month; year x.year; } return *this; }现代写法 这种写法在拷贝构造函数处理好深拷贝问题后可以很好的实现深拷贝因为我们这种写法本质是对拷贝构造函数的一个复用。 Date operator(const Date x) { // 检查自赋值避免不必要的操作 if (this ! x) { // 创建一个临时Date对象tmp并使用参数x来初始化它 Date tmp(x); // 使用std::swap来交换tmp对象的day成员和当前对象的day成员 std::swap(tmp.day, this-day); // 使用std::swap来交换tmp对象的year成员和当前对象的year成员 std::swap(tmp.year, this-year); // 使用std::swap来交换tmp对象的month成员和当前对象的month成员 std::swap(tmp.month, this-month); // 通过上述交换实际上是将tmp对象即x的副本的内容赋给了当前对象 } // 返回当前对象的引用以支持链式赋值操作 return *this; }运行结果 ✍ 析构函数 有初始化资源的函数就会有清理资源的函数。析构函数和构造函数一样它是在当前作用域结束后就会自动调用析构函数。它的函数名字不一样类名前面多了一个~。 编译器默认生成的析构函数 一般情况下编译器也会默认生成一个析构函数当我们的成员变量都没有申请资源时就不需要显示的写析构函数。 显式写的析构函数 ~Date(){year 0;day 0;month 0;}析构函数的特性 1、当前函数作用域结束后自动调用 2、无参数无返回值 3、函数名是~类名。 4、功能是清理对象中的资源而不是释放对象的空间。 5、支持显示调用构造函数不支持。 没有深拷贝导致二次释放同一空间问题 问题的引入—拷贝构造函数 前面在讲拷贝构造函数和赋值构造函数我们就对这个问题做了铺垫这个问题的本质就和标题一样内存重复释放为什么会这样本质还是万恶的值拷贝下面我们写一段代码来解释并解决这个问题。 class Stack { public:Stack(){_capacity 4;//假设开始的时候给容量设置为4_top 0;_a (int*)malloc(sizeof(int) * _capacity);if (_a nullptr){std::cout malloc Failed std::endl;exit(-1);}}~Stack(){std::cout ~Stack std::endl;_capacity 0;_top 0;free(_a);_a nullptr;} private:int* _a;int _top; // 栈顶int _capacity; // 容量 };int main() {Stack st1;Stack st2(st1);return 0; }运行结果 是的程序在这里崩溃了我们来调试一下。 可以看到st1中的_a、和st2中的_a保存的地址值是一模一样释放了两次相同空间的地址。 问题的解决—深拷贝 那么我们如何规避这种情况呢就要用到深拷贝我们可以开和st1中_a指向空间一样大小的数组并把_a指向空间的值赋值给我们的数组。 代码实现 // Stack类的拷贝构造函数 // 接收一个对Stack类型的常量引用st作为参数用于复制对象 Stack(const Stack st) { // 为栈的底层数组_a分配内存大小与源栈st的容量相同 _a (int*)malloc(sizeof(int) * (st._capacity)); // 检查内存是否分配成功 if (_a nullptr) { // 如果分配失败则输出错误信息并退出程序 std::cout 内存分配失败 std::endl; exit(-1); } // 使用memcpy函数将源栈st的底层数组内容复制到当前栈的底层数组_a中 memcpy(_a, st._a, sizeof(int) * (st._capacity)); // 复制源栈st的栈顶指针_top到当前栈 _top st._top; // 复制源栈st的容量_capacity到当前栈 _capacity st._capacity; }运行结果 此时不再报错。 调试结果 保存的地址不同指向的空间不同但是数组中的值相同完成了拷贝构造深拷贝。 赋值运算符重载函数的浅拷贝问题 使用编译器默认的值拷贝去赋值在刚刚的场景也会报错。 int main() {Stack st1;Stack st2;st2 st1;return 0; }我们可以使用赋值运算符重载函数现代写法来复用刚刚拷贝构造函数写好的深拷贝。 // Stack类的赋值运算符重载 // 接收一个对Stack类型的常量引用st作为参数用于赋值操作 Stack operator(const Stack st) { // 检查是否自赋值即当前对象与参数对象是否为同一个对象 if (this ! st) { free(_a);//释放之前_a的内存// 创建一个临时Stack对象tmp并用参数对象st初始化 Stack tmp(st); // 使用std::swap交换临时对象tmp的底层数组与当前对象的底层数组 std::swap(tmp._a, _a); // 使用std::swap交换临时对象tmp的栈顶指针与当前对象的栈顶指针 std::swap(tmp._top, _top); // 使用std::swap交换临时对象tmp的容量与当前对象的容量 std::swap(tmp._capacity, _capacity); } // 返回当前对象的引用支持链式赋值操作 return *this; }运行结果 调试结果 与预期一致。 ✍ const成员函数 使用const关键字修饰的函数放在函数括号右边叫做const成员函数const实际是修饰的this指针指向的内容所以其指向的内容不能修改。 请看下面的代码 class Date { public:Date(){year 2024;month 1;day 1;}void f() const{this-year 1;} private:int year;int month;int day; };int main() {Date x; }const对象访问的规则 1、const对象不能访问非const类型的函数但是可以构造函数和析构函数例外。这很好理解因为我们的const修饰对象对象的内容不能被修改如果你能调用非const函数在这个函数里修改了我们的成员变量那不就逻辑不自洽了嘛。 const对象访问构造函数。 也不能对析构或者构造函数使用const修饰。 const对象访问析构函数。 const对象访问非const函数是非法的。 2、const成员函数类也不能调用其它的非const的成员函数。 本质上是一样的问题const Date* const this类型不能转变为Date* const this否则它的能力就扩大了。 注意前面的const修饰的是*this表示this指向的内容具有常性后面的const修饰this指针表示this指针的值不能被修改。 非const对象访问的规则 3、但是非const成员函数内可以调用const成员函数。 程序正常退出。 4、非const对象也可以调用const函数。 3和4总结起来也是一样的Date* const this类型可以向const Date* const this类型转化。 ✍ 对普通对象的取地址运算符重载和对const对象取地址运算符重载 这两个函数一般都不需要我们显示的去写编译器会默认生成的。 我们也可以显示的写出来。 // 非const成员函数的取地址运算符重载 // 返回当前对象的地址 Date* operator() { return this; }// const成员函数的取地址运算符重载 // 返回当前对象的const地址 const Date* operator() const { return this; }但是通常只有我们有一些特殊的需求比如取出某个特定成员变量的地址时才需要自己显示的去写。 ✍ C默认构造函数提供的机制 部分内容参考博主这篇博客C默认构造函数提供的机制。 我们都知道在C98中有着这样的几种构造函数普通构造函数、析构函数、拷贝构造函数、赋值运算符重载函数。 生成这些特殊的成员函数或者不生成的规则比较复杂编译器默认生成的构造函数是有可能被删除的。 C默认构造函数是否提供的情况 如果自定义了普通构造函数和拷贝构造函数系统将不再提供默认的无参构造函数。 但是如果定义了一个赋值运算符重载函数系统还是会提供普通的无参构造函数。 2、如果自定义了一个普通的构造函数系统还会提供一个拷贝构造函数和赋值运算符重载函数值拷贝。 如果自定义了一个拷贝构造函数系统将不再提供默认的拷贝构造函数。但是会生成默认的赋值运算符重载函数。 4、如果自定义了一个赋值运算符重载函数系统就不会默认生成赋值运算符重载函数了但是其它函数还是会生成。 这里少打字了应该是无参的构造函数。 没有生成报错是这样的 如果自定义了一个析构函数系统也就不会再提供默认的析构函数。 如果类里面有没有初始化的非静态const数据成员或者引用类型的数据成员会导致默认提供的默认构造函数被删除。 当我们使用初始化列表初始化好这两个变量好发现去调用拷贝构造函数是可以的编译器默认生成了但是拷贝赋值函数却被删除了。 6.用户如果自己没有提供一个拷贝构造函数或者拷贝赋值函数编译器会隐式声明一个。
http://www.w-s-a.com/news/307518/

相关文章:

  • 平面设计师看的网站济南机场建设
  • 俄文网站开发翻译平台页面设计模板
  • 建设在线购物网站淮南电商网站建设价格
  • 龙泉市旅游门户网站建设wordpress faq插件
  • 网站的流程图贵阳做网站方舟网络
  • c 做网站开发实例wordpress 加上index
  • 济南seo网站推广搜索广告推广
  • 有关于网站建设的参考文献宁波seo网络推广公司
  • 网站设配色个人主页介绍文案
  • 网站seo相关设置优化网站建设的好处
  • 上海市建设工程安全生产协会网站郴州网站设计公司
  • 网站大型网页游戏自己搭建服务器做视频网站
  • 建立网站企业wordpress用户名密码破解
  • 网站管理助手建站教程国外网站做acm题目比较好
  • 网站开发框架排行专业网页制作服务商
  • 企业网站建设入账政务网站建设信息
  • 网络平台建设是什么江门排名优化怎么做
  • 响应式旅游网站模板下载网址做
  • 个人做网站名称可以随意更改吗惠州网站推广排名
  • 自己建设一个网站步骤网站认证怎么认证
  • 深圳建站公司开发费用沧州手机建站哪家好
  • 兰州网站设计公司排名百度怎么发布短视频
  • 大连模板开发建站泰州网站建设策划方案
  • 厦门好的网站设计局域网内建网站
  • 关键词那种网站正版网页游戏平台排行榜
  • 网站自助建设平台创建网址快捷方式
  • 坑梓网站建设包括哪些成都网站建设优创
  • 重庆网站seo公司哪家好超级优化大师
  • 成都网站建设推广详情邵阳市住房和城乡建设局网站
  • 淄博网站推广猎头公司有哪些