商业网站建设案例课程杭州seo公司哪家好

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

商业网站建设案例课程,杭州seo公司哪家好,郑州58同城,上海广告公司工资✨个人主页#xff1a; Yohifo #x1f389;所属专栏#xff1a; C修行之路 #x1f38a;每篇一句#xff1a; 图片来源 I do not believe in taking the right decision. I take a decision and make it right. 我不相信什么正确的决定。我都是先做决定#xff0c;然后把… ✨个人主页 Yohifo 所属专栏 C修行之路 每篇一句 图片来源 I do not believe in taking the right decision. I take a decision and make it right. 我不相信什么正确的决定。我都是先做决定然后把事情做好。 文章目录前言正文默认成员函数构造函数️默认构造函数️新增补丁析构函数️默认析构函数拷贝构造函数️默认拷贝构造函数️无穷递归️浅拷贝️深拷贝️小结运算符重载️operator操作符️使用注意赋值重载函数️默认赋值重载函数️深度赋值️注意事项const修饰️修饰*this取地址重载函数const修饰的取地址重载函数总结前言 祖师爷在设计 C 中的类时规定每个类中都有六个默认成员函数即使我们不主动写编译器也会自动生成这些成员函数就是神秘的天选之子不仅神秘而且还很强大可以这么说类和对象的是否学懂取决于对这几个天选之子的理解程度。本文将会逐一介绍这几个默认成员函数跟随我的脚步一起揭开他们的神秘面纱 注意以上函数都需要加上 “默认” 前缀因为编译器自动生成并调用的是默认成员函数 正文 默认成员函数 祖师爷规定每个类中都必须有这六个默认成员函数 默认构造函数 重要默认析构函数 重要默认拷贝构造函数 较重要默认赋值重载函数 较重要默认取地址重载函数 一般默认const取地址重载函数 一般 虽说都是祖师爷钦定的天选之子但最后两个相对来说比较简单因此介绍也会比较少 默认成员函数规则比较多尤其是构造和析构当初祖师爷在设计的时候部分地方设计欠佳导致后人在学习 C 时额外增加了不少学习成本 下面就来看看祖师爷是如何设计的、出了什么问题、以及是怎么解决的 构造函数 构造函数是祖师爷首先钦定的天选之子 构造函数诞生的目的是为了减少频繁手动初始化的问题将初始化这个事情变成自动化处理 将C语言和C都看作车辆初始化操作看作换挡可以这样认为 C语言依赖于手动操作就像手动挡车辆有驾驶乐趣但比较麻烦C面向对象自动操作就像自动挡车辆上手简单驾驶难度低 我们是可以自己编写构造函数的祖师爷给了我们这个权力他钦定的天选之子是默认构造函数由编译器自动生成并供类默认调用的下面来看看编写构造函数的规则 本文介绍的函数都属于特殊函数规则和普通函数不同 构造函数创建规则 函数名和类名相同不需要返回值甚至连 void 都不需要写对象实例化时编译器自动调用默认构造函数构造函数支持重载即可以存在多个构造函数但默认构造函数只能有一个 构造函数还有一种特殊形式默认构造函数 语法规定不带参数或参数为全缺省的构造函数称为默认构造函数 默认构造函数有两种写法推荐全缺省参数的形式 class Date { public://特别注意默认构造函数只允许存在一个形式//一般推荐使用形式二全缺省参数//因为这样方便后续初始化时指定值默认构造函数形式一不带参数//Date()//{// _year 1970;// _month 1;// _day 1;//}//默认构造函数形式二参数为全缺省Date(int year 1970, int month 1, int day 1){_year year;_month month;_day day;cout Date(int year 1970, int month 1, int day 1) endl;}//其他普通构造函数只要与默认构造函数构成重载都合法Date(double b){_year 2023;_month 2;_day 9;cout Date(double b) endl;}//……private:int _year;int _month;int _day; };严格来说现阶段的初始化方式不规范此时为赋值 正确的初始化方式是使用初始化列表当然这个东西在下篇介绍 出自 《Effective C》 当构造函数写好后我们就可以这样使用 int main() {//调用默认构造函数d1 初始化为1970 1 1Date d1; //相当于 Date d1(); 但不能这样写//因为调用的是全缺省参数的默认构造函数我们也可以传参数//Date d1(2001, 1, 1); //这种初始化也是合法的//调用自定义构造函数Date d2(1.1);return 0; }构造函数允许重载因此调用不会冲突运行结果如下 ️默认构造函数 如果我们没写默认构造函数 就会由编译器自动生成这是祖师爷制定的规则假如我们写了同时也满足默认构造函数的需求编译器就会以我们写的为准并转而调用我们写的默认构造函数 注默认构造函数就是调用时不需要传参的构造函数 默认构造函数形式一不带参数//Date()//{// _year 1970;// _month 1;// _day 1;//}//默认构造函数形式二参数为全缺省Date(int year 1970, int month 1, int day 1){_year year;_month month;_day day;cout Date(int year 1970, int month 1, int day 1) endl;}这里暂时无法给大家演示编译器自动生成并调用的现象 因为祖师爷在设计默认构造函数时埋下了一个坑 默认构造函数不对内置数据类型做处理如 int 、double、char等至于自定义类型默认构造函数会去调用属于它们的初始化函数(默认构造函数) 注意数据类型主要分为两类 内置类型和自定义类型 简言之默认构造函数有点像不干实事的函数 假设我们的类中只有内置类型那么默认构造函数真就什么都没有做出现自定义类型时也只会主动去调用它的默认构造函数至于自定义类型的默认构造函数干了什么类的默认构造函数是不管的 因祖师爷设计疏忽而留下的坑为后世学习C增加了阻力 这么看来这个天选之子似乎没有什么用默认构造函数还得我们自己编写 不过在有些场景下默认构造函数很有用 题目用栈实现队列需要在队列这个类中调用两个栈类实现队列类此时我们只需要写好栈的默认构造函数队列类的默认构造函数不需要写因为自动生成的会去调用自定义类型的默认构造函数即栈的默认构造函数显然是存在的 为了解决祖师爷留下来的坑委员会在C11标准中新增了一个补丁声明时给缺省值 ️新增补丁 这个补丁是新标准中的可能部分老编译器不支持 具体操作很简单在成员变量声明时将内置类型给上缺省值调用编译器生成的默认构造函数时就会以这些缺省值来初始化成员变量达到初始化的效果 注意此时给的是缺省值并非在声明阶段赋值类中成员变量为声明阶段 class Date { private:int _year 2023; //在内置类型声明时给上缺省值int _month 2; //这样即使调用生成的默认构造函数int _day 9; //也能达到初始化的效果 };有了补丁辅助我们后就可以看看编译器是否调用了默认构造函数 在打了补丁的情况下实例化一个对象可以看到效果如下 C11中的补丁可以解决内置类型不初始化这个问题但相对来说全缺省参数形式的默认构造函数更加实用不仅能初始化还能指定初始化值 注意这个补丁是为内置类型准备的对于自定义类型默认构造函数会去调用属于它的默认构造函数 在涉及开辟空间的初始化行为时可以先给 nullptr再到默认构造函数体内开辟空间 析构函数 析构函数就是另一个天选之子了 构造函数 解决频繁初始化操作 析构函数 解决频繁销毁操作 不难看出这是两兄弟一个负责做菜一个负责洗碗 此时仍然是手动挡和自动挡的区别 析构函数和构造函数师出同门规则也都差不多 析构函数创建规则 函数名在类名的前面加上~也没有返回值连 void 都不需要写对象声明周期结束时编译器会自动调用默认析构函数析构函数不支持重载毕竟不能对同一个对象销毁多次 析构函数也有一种特殊形式默认析构函数 不过因为析构函数不支持重载所以默认与否已经不重要了如果我们写了编译器就用我们写的默认析构函数否则就用编译器自动生成的 默认析构函数也存在默认构造函数的坑对内置类型不作处理 ️默认析构函数 如果我们没写编译器会自动生成默认析构函数假如我们写了编译器就会用我们写的 注默认析构函数是在对象生命周期结束时自动调用 class Date { public://析构函数写法比较特殊需要多加注意~Date(){//假设动态开辟了空间//是需要在析构函数中释放的_year _month _day 0;} private:int _year 2023;int _month 2;int _day 9; };同默认构造函数一样默认析构函数对内置类型也不处理对自定义类型会去调用属于它的默认构造函数 默认构造函数不难写普通自定义类型是否释放问题不大但涉及动态内存开辟时如果不释放内存就会发生内存泄漏问题因此当我们的对象涉及动态内存开辟时需要自己编写默认析构函数确保安全问题。 ~Test() {//假设 pa 为动态开辟的空间//需要释放free(pa);pa nullptr; }拷贝构造函数 拷贝构造函数 算是 构造函数 的远房亲戚因为它们的函数名一样不过参数不同构成重载 对于内置类型我们可以通过 完成拷贝对于自定义类型规则较多拷贝需要我们自己完成因此拷贝构造函数应运而生 为何自定义类型不能直接赋值 因为自定义类型种类繁多比如栈、队列、树、图数据结构很复杂尤其是涉及空间开辟问题时不能简单通过指针赋值完成拷贝这样会导致重复析构问题正确做法是 开辟空间-拷贝数据-更新指向-完成拷贝 int a 10; int b a; //内置类型简单拷贝(赋值)拷贝构造函数实现也很简单 class Date { public://拷贝构造函数此时是简单拷贝只能用于非动态内存开辟的空间//拷贝构造函数函数名与构造函数相同不过参数类型为类Date(const Date d){//d 拷贝给 this_year d._year;_month d._month;_day d._day;} private:int _year 2023;int _month 2;int _day 9; };使用时有以下两种用法 Date d1; //将 d1 拷贝给 d2 和 d3 Date d2(d1); //法一Date d3 d1; //法二既然是天选之子之一编译器也会生成默认拷贝构造函数 ️默认拷贝构造函数 默认拷贝构造函数 是个懂事的函数对于内置类型它不再持有偏见也就是说默认拷贝构造函数能完成简单内置类型的拷贝操作 正常情况下即成员变量不涉及空间开辟时拷贝构造函数 没有必要写用编译器自动生成的足够了 对于涉及空间开辟的一定要写默认拷贝构造函数 class SeqList { public://现在编写一个涉及空间开辟的拷贝构造函数SeqList(const SeqList tmp){_pa (int)malloc(sizeof(int) * _capacity);if (nullptr _pa){cout malloc fail endl;exit(-1); //失败直接退出程序}//将 tmp 空间中的值拷贝到 *this 中memcpy(_pa, tmp._pa, sizeof(int) * _size);_size tmp._size;_capacity tmp._capacity;}private:int* _pa nullptr;int _size 0;int _capacity 4; //动态顺序表 }; 默认拷贝构造函数实现比较简单但有一个值得注意的大问题无穷递归 ️无穷递归 所谓无穷递归问题就是指设计拷贝构造函数时参数没有设为引用 如下所示 SeqList(SeqList tmp) {//此时必然会引发无穷递归问题//…… }问题出现原因值传递需要先生成临时变量再传递而生成临时变量这个行为本身就是在调用拷贝构造函数 也就是说此时我们在实现拷贝构造函数但参数又需要拷贝构造函数 这让编译器很难堪 大力士无法举起自己光靠金针菇也无法完成卡Bug行为 解决方法因为待拷贝对象本来就已经存在此时可以使用引用避免产生临时变量再加以 const 修饰保护待拷贝对象 因此正确的拷贝构造函数应该这样写 SeqList(const SeqList tmp) {//有效避免无穷递归问题//…… }️浅拷贝 浅拷贝 就是简单的逐字节序拷贝 浅拷贝可能出现空间共用的情况
浅拷贝可能引发对同一块空间的重复析构问题 浅拷贝不可取尤其是在面对复杂数据结构时 ️深拷贝 深拷贝需要我们自己实现 深拷贝 在面对空间问题时会先给 对象2 开辟一块同样大的空间再将 对象1 空间中的数据拷贝过来 深拷贝中两个对象的空间是独立的、互不干扰的 深拷贝才是众望所归 当成员涉及复杂数据结构、空间开辟时就需要写出默认拷贝构造函数 ️小结 构造函数家族至此就介绍完成了简单小结一下 类型用途处理情况默认析构函数初始化对象不对内置类型作处理默认析构函数销毁对象也不对内置类型作处理默认拷贝构造函数拷贝对象只能对简单内置类型做处理 何时需要自己写默认析构函数 当我们写出默认拷贝函数完成复杂对象的拷贝时就证明需要默认析构函数来释放对象 小技巧 在函数传参与返回时如果对象生命周期足够长就可以考虑使用引用的方式避免参数走拷贝构造-生成临时变量-再传递的路线提高程序运行效率 特别注意 默认拷贝构造函数与默认构造函数名相同当我们只写拷贝而不写构造时编译器就会报错因为此时的拷贝会被误以为是默认构造函数 也就是说默认拷贝构造函数存在的前提是默认构造函数已存在 运算符重载 C支持运算符重载运算符重载使得自定义类型间的符号运算变成可能 比如 int a 1; int b 2; a - b; //合法Date d1; Date d2; d1 - d2; //非法此时需要通过运算符重载解决这个问题//解决方法如下 const Date operator-(const Date d) {//简单演示逻辑存在问题可以忽略Date tmp(this); //调用拷贝构造tmp._year - d._year; //内置类型可以正常相减tmp._month - d._month; //同理tmp._day - d._day; //同上return tmp; //tmp 为临时变量不能传引用返回 }此时就可以正常使用 d1 - d2 了 注运算符重载和函数重载没有关系 ️operator操作符 operator 译为运算符是C中新的关键字operator 的作用很简单实现自定义类型的运算 使用规则 operator 函数中的操作数取决于参数个数operator 一般写在类中方便通过 this 指针访问成员变量写在类中时this 指针就算一个隐藏参数operator也可以写在类外此时会发生无法访问成员变量问题可以这样解决 将成员变量设为 public 不安全通过函数获取类中的成员变量值 麻烦设置为友元函数也比较麻烦写在类中最简单、省事而且还可以使用 this 指针
运算符重载是这样用的 int main() {//注此时只是演示日期类的减法不能这样写Date d1(2023, 2, 9);Date d2(2022, 2, 9);Date d3 (d1 - d2); //结果为 1 0 0//此时的调用相当于 d1.operator-(d2) //d1 作为 this 对象传递//也可以这样使用Date d4 operator-(d1, d2);return 0; }基于运算符重载我们可以干很多事情比如直接通过 [] 访问类中的成员实现两个对象的快速运算等操作 ️使用注意 operator 虽然很好但也有很多使用规则 operator 操作符就是函数名不能与非操作符链接参数中必须有一个自定义类型对于内置运算符不能改变其含义成员函数的第一个参数为 this有五个运算符不支持重载 .
稀有运算符很少见:: 域作用限定符sizeof 操作符? : 三目运算符. 访问成员符 为何运算符能实现重载 跟函数重载同理保证函数修饰名不同构成重载 下面是我测试出的部分运算符重载修饰规则 基于运算符重载我们可以介绍第四个天选之子赋值重载函数 赋值重载函数 赋值重载函数的实现原理就是运算符重载 此时重载的运算符是
赋值重载的目的将 d1 对象赋值给 d2非拷贝构造d1、d2均已存在 class Date { public://赋值重载函数Date operator(const Date d){//能用引用的地方就用引用//避免去走拷贝构造函数//如果传入的是两个相同值没必要再执行赋值if (this d){return *this;}_year d._year;_month d._month;_day d._day;//返回赋值完成的值即 *thisreturn *this;} private:int _year 2023;int _month 2;int _day 9; };为何传引用 两个对象都已存在使用引用提高效率 为何判断相同 避免资源浪费当类的成员变量很多时假如出现 d1 d1 d1 这种情况时可以有效避免资源浪费 为何返回 this
因为可能出现重复赋值的情况如 d1 d2 d3 赋值重载函数不难实现只是需要注意的地方很多 ️默认赋值重载函数 祖师爷在实现默认赋值重载函数时实现的几乎已经很好了无论是内置类型还是自定义类型都会处理 不过默认赋值重载函数仍然是基于字节序的浅赋值在面对空间开辟时仍然需要我们自己编写深度赋值重载函数否则就会发生重复析构问题 ️深度赋值 深度赋值的实现和深拷贝几乎一模一样这里就不加以赘述 一但对象中涉及动态内存开辟必须自己实现深度拷贝 ️注意事项 拷贝构造 和 赋值重载存在本质区别一个是对象尚未实例化另一个是两个对象都已存在 当两个对象都被创建并发生赋值行为时才叫做赋值重载 Date d1; Date d2(d1); //拷贝构造d2 未存在 Date d3 (d1 - d2); //赋值重载d1、d2 已存在涉及空间资源管理时必须实现深度拷贝 设计赋值重载函数时充分利用引用提高效率 const修饰 const 修饰可以提高程序的健壮性 const 常被用来修饰引用、指针 当被指向对象为常量或临时变量时必须使用 const 修饰避免出现权限放大问题 //int
pa 10; //错误10 具有常性 const int* pa (const int*)10; //成功此时 pa 为常量指针//int pb 20; //错误20 具有常性 const int pb 20; //成功引用此时 pb 20const 一般用来修饰指针参数或引用参数确保参数在使用过程中不被修改 ️修饰*this 引入两个日期 d1、d2d1 - d2 时d1 需要被修改但 d2 不能被修改因此实现 operator- 时参数 d 为 const Date 类型 我们在实现函数时存在这种情况 确保 this 不被修改即 this 指针指向内容不被修改 this 指针太危险了如果不加以保护的话可能实现者的不经意行为会导致严重的后果 class Date { public://实现简单的打印函数void Print(){ cout _year 年;cout _month 月;cout _day 日;cout endl//有可能不小心出现这样的情况_year _month _day;//此时该对象就危险了成员变量全被改了}private:int _year 2023;int _month 2;int _day 9; };this 指针给了我们足够的自由我们在使用时也应该尊重它 class Date { public://实现简单的打印函数//原指针类型为 Date const 只允许指向对象//修改指针类型为 const Date* const 双重保护void Print() const{ cout _year 年;cout _month 月;cout _day 日;cout endl//有可能不小心出现这样的情况_year _month _day; //此时会报错因为 this 指针类型为 const Date* const//此时该对象就危险了成员变量全被改了}private:int _year 2023;int _month 2;int _day 9; };除了上述情况外还可能存在这种情况 const Date d1; //此时 d1 具有常性普通的 this 指针无法调动需要使用 const 指针总之const可以修饰this指针起到保护和权限平移交接的效果 取地址重载函数 接下来简单介绍一下剩下两个天选之子 取地址重载函数 获取当前对象的地址 class Date { public:Date* operator(){return this;} private:int _year 2023;int _month 2;int _day 9; };const修饰的取地址重载函数 const修饰的取地址重载函数 获取 const 修饰对象的地址 class Date { public:const Date* operator() const{return this;} private:int _year 2023;int _month 2;int _day 9; };这两个默认成员函数都很简单使用编译器默认生成的就够了除非我们不想让别人获取到当前地址直接手动设置每次都返回 nullptr 当然这种情况几乎不存在 开发者何必为难开发者 总结 以上就是关于 类和对象中 的全部内容了本文主要侧重点为 六大天选之子以及编译器自动生成的默认成员函数何时用编译器的、何时用我们自己写的都是有讲究的部分成员函数规则多、实现麻烦需要多加练习以加深理解。这里推荐日期类的实现练习能让我们对类和对象有一个更深层次的理解关于日期类的实现我将会在下篇文章中介绍敬请期待 如果你觉得本文写的还不错的话期待留下一个小小的赞你的支持是我分享的最大动力 如果本文有不足或错误的地方随时欢迎指出我会在第一时间改正 …相关文章推荐 类和对象上 C入门基础