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

一个域名怎么做网站自己建设手机网站首页

一个域名怎么做网站,自己建设手机网站首页,北京做网站s,多语言网站怎么实现目录 1.C相对于C语言的一些不同的小语法 1.1命名空间 1.2C输入输出 1.3缺省参数 1.4函数重载 1.5引用 1.6内联函数 1.7auto 1.8nullptr 2.类的引入 2.1类的内部 2.2this指针 2.3类的默认成员函数 2.3.1构造函数 2.3.2析构函数 2.3.3拷贝构造 2.4运…目录 1.C相对于C语言的一些不同的小语法 1.1命名空间 1.2C输入输出 1.3缺省参数 1.4函数重载 1.5引用 1.6内联函数 1.7auto 1.8nullptr 2.类的引入 2.1类的内部 2.2this指针         2.3类的默认成员函数 2.3.1构造函数 2.3.2析构函数 2.3.3拷贝构造 2.4运算符重载 2.4.1赋值运算符格式 2.4.2 赋值运算符重载格式 2.4.3前置和后置 2.4.4const成员 2.5初始化列表 2.6构造函数的隐式类型转换和explicit关键字 2.7static成员 2.8友元 2.8.1友元函数 2.8.2友元类 1.C相对于C语言的一些不同的小语法 在讲C之前我们应该要了解一下C的一些小语法 1.1命名空间 C中我们需要使用的变量名称都是很多很多的在这么多的变量中难免会出现一些小情况会导致名字相同含义和作用不同的两个变量所以在C中就引入了命名空间这个概念namesapce关键字也因此而生 假设我们在C语言中我们写下面这段代码 #include stdio.h #include stdlib.h int rand 10; // C语言没办法解决类似这样的命名冲突问题所以C提出了namespace来解决 int main() {printf(%d\n, rand);return 0; }我们会发现我们的语法会报错因为头文件中stdlib中有rand的定义了这样我们就不能再使用这个变量名要另外取名字。 但是在C中引入命名空间之后我们就可以使用这个rand并作为不一样的作用使用做法是吧rand作为一个变量定义在一个命名空间中这样我们使用这个域作用限定符就可以访问不同域中的同名变量了 namespace s1 {int rand 100; } namespace s2 {int rand 0; } int main() {s1::rand;s2::rand; } 另外命名空间可以嵌套例如我们可以在域s1中再定义一个域s2但是我们在访问s2的时候要先访问s1 namespace s1 {int rand 100;namespace s2{int rand 0;} }int main() {s1::rand;s1::s2::rand; } 除此之外我们可以在一段代码中同时定义多个相同名字的命名空间但是最后它们都会被划分到同一个命名空间中。 命名空间的使用方法主要有三种 第一种就是前面提到的使用加命名空间名称及作用域限定符即可访问对应的函数或者变量我们在C标准库std中就有我们经常使用的cin和cout但是使用它们还需要包含iostream这个头文件 #include iostream int main() {std::cout Hello World ;int a;std::cin a;std::cout a; } 第二种就是使用using把某些成员引入比如 #include iostream using std::cout; using std::cin; int main() {cout Hello World;int a;cin a;cout a;return 0; } 第三种就是使用using namespace 命名空间名称 引入把命名空间整个展开直接使用内部的所有变量和函数也是我们经常看到的“using namespace std”的由来 #include iostream using namespace std; int main() {cout Hello World;int a;cin a;cout a;return 0; } 第三种过于粗暴一般只在竞赛和平时的小练习中使用在大型项目中很少会这么干命名空间的展开会造成很严重的问题而且我们c在这方面相对于c语言的优势也就没有了。 1.2C输入输出 上面我们提到过的cin和cout就是我们c中的输入输出不过更专业的名词应该是流插入和流提取。 std是C标准库的命名空间名C将标准库的定义实现都放到这个命名空间中 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时必须包含 iostream 头文件 以及按命名空间使用方法使用std。 cout和cin是全局的流对象endl是特殊的C符号表示换行输出他们都包含在包含 iostream 头文件中。 是流插入运算符是流提取运算符。 使用C输入输出更方便不需要像printf/scanf输入输出时那样需要手动控制格式。 C的输入输出可以自动识别变量类型。 实际上cout和cin分别是ostream和istream类型的对象和也涉及运算符重载等知识 这些知识我们我们后续才会学习所以我们这里只是简单学习他们的使用。后面我们还有有 一个章节更深入的学习IO流用法及原理。 cin会自动识别格式这也是c比较方便的一个点。另外和cout一起使用的还有一个就是endl相当于换行符在cout后面再加上 endl就可以了 #include iostream using namespace std;int main() {int a;double b;char c;// 可以自动识别变量的类型cina;cinbc;coutaendl;coutb cendl;return 0; } 1.3缺省参数 在C中缺省参数就是在函数定义或者声明的时候给的一个缺省值如果在调用函数的时候没有传入参数就可以使用这个默认的参数进行参数初始化如果有传入参数那就优先考虑传入的实参。 void Func(int a 0) {coutaendl; } int main() {Func();     // 没有传参时使用参数的默认值Func(10);   // 传参时使用指定的实参return 0; } 缺省参数中又有全缺省和半缺省全缺省就是多个参数全部给缺省值的情况 void Func(int a 10, int b 20, int c 30){couta aendl;coutb bendl;coutc cendl;}半缺省也好理解就是不给全部缺省值不过需要注意的是我们的班缺省参数只能从优往左给缺省值不能隔着 void Func(int a, int b 10, int c 20){couta aendl;coutb bendl;coutc cendl;} 像下面这些情况都是错误的 半缺省参数都是从右往左给的不过一般情况下我们一律使用全缺省参数的情况似乎更多一点。 另外我们的缺省参数不能再函数的定义和函数的声明中同时给就算是给的一样的值也不行 除此之外缺省值还必须是常量或者全局变量就没有更多的注意事项了。 1.4函数重载 函数重载有点类似中文中的“一词多义”只不过在C中这是针对函数的修饰说明函数重载C中体现在允许同一作用域中声明几个功能类似的同名函数这些同名函数的形参列表(参数个数或类型或类型顺序)不同常用来处理实现功能类似数据类型不同的问题。 #includeiostream using namespace std; // 1、参数类型不同 int Add(int left, int right) {cout int Add(int left, int right) endl;return left right; } double Add(double left, double right) {cout double Add(double left, double right) endl;return left right; } // 2、参数个数不同 void f() {cout f() endl; } void f(int a) {cout f(int a) endl; } // 3、参数类型顺序不同 void f(int a, char b) {cout f(int a,char b) endl; } void f(char b, int a) {cout f(char b, int a) endl; } int main() {Add(10, 20);Add(10.1, 20.2);f();f(10);f(10, a);f(a, 10);return 0; } 上面的这些函数就构成函数重载我们以后也会遇到很多。  但是我们C中是不支持使用返回值不同但是参数相同的函数比如下面这个函数 不过我们想要让它合理就只要改其中一个的参数类型或者参数数量像这样 究其原因其实是在C语言中函数的调用只有函数名这样一个参考但是在C中调用函数就变成了函数名每个参数类型这样就导致了我们的C可以实现函数重载而C语言不能。另外函数重载和给函数缺省值在实际写代码的过程中都是很好用的技巧也算是C的优胜处吧。 1.5引用 引用我们也叫做“取别名”就是给一个人去另外一个名字实际代表的是同一个人而且一个人可以有多个别名。在C中我们可以给变量取别名在编译器处理过程中编译器不会再开辟新的空间而是使用别名和原来变量使用同一块空间 引用的操作就是类型 引用变量名(对象名) 引用实体另外我们需要注意的是引用类型必须和引用实体是同种类型的。 前面我们说“给一个人去另外一个名字实际代表的是同一个人而且一个人可以有多个别名”所以在引用中我们也可以给同一个变量给多个引用。 我们在使用引用的时候也会相应的映射到原来变量即我们改变引用的值原始的变量的值也可以被改变。而且我们还可以给别名取别名也是同样的效果。 而引用的底层还是我们在C语言中接触过的指针不过这里就不是指针这个概念而是更方便的引用了并且还更加节省空间我们可以使用引用做很多事情。 //1. 做参数 void Swap(int left, int right) {  int temp left;  left right;  right temp; } //2. 做返回值 int Count() {  static int n 0; n;   // ...  return n; } 使用引用去代替指针的作用我们的代码会更加简洁好理解并且还能一定程度上节省空间 但是这里有一段代码 int Add(int a, int b) {int c a b;return c; } int main() {int ret Add(1, 2);Add(3, 4);cout Add(1, 2) is : ret endl;return 0; } 这段代码就揭示了引用的局限性看一下输出 我们发现当引用作为返回值的时候好像输出的并不是我们想要的结果。其实这里也是前面说过的当使用引用返回局部变量c而c又在出了函数作用域之后销毁了所以我们得到的ret就变成了c销毁之后的值也就是一个随机值所以我们没有得到想要的结果在我们使用引用的过程中我们要尽量避免这样的情况发生。 以值作为参数或者返回值类型在传参和返回期间函数不会直接传递实参或者将变量本身直 接返回而是传递实参或者返回变量的一份临时的拷贝因此用值作为参数或者返回值类型效 率是非常低下的尤其是当参数或者返回值类型非常大时效率就更低。所以可以使用传引用返回的时候我们就尽量使用这样一个更加高效的方法实现不过还是要注意变量出作用域销毁的问题。 1.6内联函数 内联函数一般以inline修饰编译时C编译器会在调用内联函数的地方展开没有函数调用建立栈帧的开销内联函数提升程序运行的效率。 inline是一种以空间换时间的做法如果编译器将函数当成内联函数处理在编译阶段会 用函数体替换函数调用缺陷可能会使目标文件变大优势少了调用开销提高程序运 行效率。      inline对于编译器而言只是一个建议不同编译器关于inline实现机制可能不同一般建 议将函数规模较小(即函数不是很长具体没有准确的说法取决于编译器内部实现)、不 是递归、且频繁调用的函数采用inline修饰否则编译器会忽略inline特性。 inline不建议声明和定义分离分离会导致链接错误。因为inline被展开就没有函数地址 了链接就会找不到。 1.7auto 随着程序越来越复杂程序中用到的类型也越来越复杂经常体现在 1. 类型难于拼写 2. 含义不明确导致容易出错 #include string #include map int main() {std::mapstd::string, std::string m{ { apple, 苹果 }, { orange, 橙子 }, {pear,梨} };std::mapstd::string, std::string::iterator it m.begin();while (it ! m.end()){//....}return 0; } 上面的代码中std::map::iterator 是一个类型但是该类型太长了特别容易写错。也许我们可以使用typedef重定义类型但是这要求我们清楚的了解它的类型才不会用错然而有时候要做到这点并非那么容易因此C11给auto赋予了新的含义即自动识别变量类型。 这样我们就不用想大半天然后最后还会写错了这不仅大大简化了我们的代码还提高了我们的正确率。但是我们使用auto定义一个变量之前必须要给它初始化。 注意事项 1.在使用auto定义指针变量的时候我们可以加上“ * ”也可以不加但是声明引用类型的时候必须加 2.当在同一行声明多个变量时这些变量必须是相同的类型否则编译器将会报错因为编译 器实际只对第一个类型进行推导然后用推导出来的类型定义其他变量。 void TestAuto() {auto a 1, b 2; auto c 3, d 4.0;  // 该行代码会编译失败因为c和d的初始化表达式类型不同 } 3.auto不能用来作为函数的参数也不能直接用来声明数组 // 此处代码编译失败auto不能作为形参类型因为编译器无法对a的实际类型进行推导 void TestAuto(auto a) {}void TestAuto() {int a[] {1,2,3};auto b[] {456}; } 4.auto在实际中最常见的优势用法就是跟以后会讲到的C11提供的新式for循环还有 lambda表达式等进行配合使用。 这里讲一下新式for循环 这是我们通常遍历数组的做法 void TestFor() {int array[] { 1, 2, 3, 4, 5 };for (int i 0; i sizeof(array) / sizeof(array[0]); i)array[i] * 2;for (int* p array; p array sizeof(array)/ sizeof(array[0]); p)cout *p endl; }对于一个有范围的集合而言由程序员来说明循环的范围是多余的有时候还会容易犯错误。因 此C11中引入了基于范围的for循环。for循环后的括号由冒号“ ”分为两部分第一部分是范围内用于迭代的变量第二部分则表示被迭代的范围。 它的基本格式如下 for (declaration : expression) {// loop body } 这里declaration 是每次循环开始时声明和初始化的变量expression 是一个可以产生范围例如数组、容器、字符串等的表达式 。 实际运用就是这样 void TestFor() {int array[] { 1, 2, 3, 4, 5 };for(auto e : array)e * 2;for(auto e : array)cout e ;return 0; } C11中把auto作为遍历数组的方式有点类似python不过auto作为遍历数组还是有很多不足的不如不能倒序也不能从中间开始数。 并且像下面这段代码 void TestFor(int array[]) {for(auto e : array)cout e endl; } 由于范围不确定传入的array只是首元素地址范围不明确就会导致报错。 1.8nullptr 在C语言和C98中NULL其实是一个宏有这样的定义 #ifndef NULL #ifdef __cplusplus #define NULL   0 #else #define NULL   ((void *)0) #endif #endif所以上面我们的代码中原本是想要用f(0)调用fint用fNULL调用fint*的但是其实它全部被用成了fint这就是因为NULL被宏定义成0导致的后果指代不明。 因此在C11中多了一个专门用来定义空指针的nullptr横空出世nullptr是一个专门用于空指针定义的void*类型变量并且在C11中nullptr的使用不需要包含头文件它是作为一个关键字来使用的。 为了提高代码的健壮性在后续表示指针空值时建议最好使用nullptr。 2.类的引入 C语言结构体中只能定义变量在C中结构体内不仅可以定义变量也可以定义函数。比如 之前在数据结构初阶中用C语言方式实现的栈结构体中只能定义变量现在以C方式实现 会发现struct中也可以定义函数。 不过在C中对于这种结构体的定义C一般都是使用class类来定义的。 class className {// 类体由成员函数和成员变量组成};  // 一定要注意后面的分号 class为定义类的关键字ClassName为类的名字{}中为类的主体注意类定义结束时后面分 号不能省略。 类体中内容称为类的成员类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。 2.1类的内部 类的内部函数一般叫做类的成员函数如果一个成员函数在类中定义为了效率编译器一般会把它当做内联函数处理。         当然也可以声明和定义分离这种情况下也是会被当做内联函数处理的。在类里声明在其他地方定义不过在外定义的时候就要加上类名与类域限定符否则无法访问这个函数。 一般情况下更期望采用第二种方式。注意上课为了方便演示使用方式一定义类尽量使用第二种。 然后就是类的访问限定符和封装用类将对象的属性与方法结合在一块让对象更加完善通过访问权限选 择性的将其接口提供给外部的用户使用。 【访问限定符说明】 1. public修饰的成员在类外可以直接被访问 2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的) 3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止 4. 如果后面没有访问限定符作用域就到 } 即类结束。 5. class的默认访问权限为privatestruct为public(因为struct要兼容C) 一般来说我们的成员变量都会定义在private中并且成员变量一般都会加上_修饰比如 class Date { public : private:int _year;int _month;int _day; }; 2.2this指针         下面有一段代码 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, d2;d1.Init(2022, 1, 11);d2.Init(2022, 1, 12);d1.Print();d2.Print();return 0; } 我们这里定义了d1和d2Date类中有 Init 与 Print 两个成员函数函数体中没有关于不同对象的区分那当d1调用 Init 函 数时该函数是如何知道应该设置d1对象而不是设置d2对象呢 C中通过引入this指针解决该问题即C编译器给每个“非静态的成员函数“增加了一个隐藏 的指针参数让该指针指向当前对象(函数运行时调用该函数的对象)在函数体中所有“成员变量” 的操作都是通过该指针去访问。只不过所有的操作对用户是透明的即用户不需要来传递编 译器自动完成。 1. this指针的类型类类型* const即成员函数中不能给this指针赋值。 2. 只能在“成员函数”的内部使用 3. this指针本质上是“成员函数”的形参当对象调用成员函数时将对象地址作为实参传递给 this形参。所以对象中不存储this指针。 4. this指针是“成员函数”第一个隐含的指针形参一般情况由编译器通过ecx寄存器自动传 递不需要用户传递 2.3类的默认成员函数 一个类中什么成员都没有简称为空类。在一个空类里面自动什么都没有吗 class Date {}; 比如上面的日期类Date它的内部并不是什么都没有而是有很多的默认成员函数 下面我们将逐步了解这些默认成员函数。 2.3.1构造函数 这里的构造函数是一个函数的名称是一种初始化成员变量的函数并且它在我们构造一个类的时候自动调用 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(2022, 7, 5);d1.Print();Date d2;d2.Init(2022, 7, 6);d2.Print();return 0; } 上面定义了一个类包含一个成员函数Init我们要使用这个Init作为初始化函数为成员变量赋初始值。这个是我们使用很原始的思想为它初始化但其实我们还有更简便的方法。 在C类中默认定义了一个构造函数无返回值不用写返回类型名字和类名一样在定义一个类之后会自动调用。这个函数可以由编译器默认实现当然也可以由我们自己显式实现。 在这里我们显式实现构造函数我们可以看到确实是自动调用了构造函数 在默认的构造函数中并没有给我们的成员变量赋值如果我们想要给它赋值我们需要自己实现一个构造函数并赋初值。 但是在我们自己写构造函数的时候函数也是有需要注意的地方首先这个构造函数的参数可有可无其次我们的构造函数如果有参数的话要我们在定义一个类的实例的时候就要带上这个参数否则就会报错。 如果要解决这个报错我们就要在实例化的时候就给一个参数这样就可以解决了。 不过像这种情况我们其实可以考虑到之前学过的缺省参数的用法给所有参数一个缺省参数这样我们就不用自己给参数了如果需要参数的时候还可以写上去默认会优先使用我们给的新参数 Date(int year 2000,int month 1,int day 1){_year year;_month month;_day day;} 构造函数其实是支持重载的其实我们再写一个重载函数也可以不过直接使用全缺省的构造函数是最简单的。 但是重载需要注意无参数构造和全缺省构造函数也构成重载但是在无参数传入的时候这种重载会被认定为指代不明因为编译器会不知道使用哪一个函数所以会报错。 Date(){_year 1900;_month 1;_day 1;}Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;} //Date d; - 会报错 //Date d1(2020,1,1); -不会报错 当用户定义了构造函数之后编译器就不会再生成默认构造函数了。 对于初始化在C11后对于默认生成的构造函数无法初始化的情况做了一个补丁即内置类型成员变量在声明的时候可以给默认值 class Date { public://... private:int _year 2020;int _month 1;int _day 1; }; 这里的给值相当于给缺省值因为这里的所有成员变量都是声明而不是定义所以这里所有的值都是缺省值。 2.3.2析构函数 有了建造成员变量自然就需要销毁成员变量和成员函数所以这里又是一个默认的成员函数叫做析构函数。析构函数的很多性质和构造函数差不多比如自动调用可以由用户显式实现等。不过这里还是要讲一下它的特点 析构函数名是在类名前加上字符 ~。无参数无返回值类型。一个类只能有一个析构函数。若未显式定义系统会自动生成默认的析构函数。注意析构 函数不能重载对象生命周期结束时C编译系统系统自动调用析构函数。 一般来说在一些数据结构中比如顺序表链表等使用用户显式实现的析构函数就是很好用的一般的普通类编译器默认析构函数其实也是够用的不过在使用数据结构中才可以体会到它的魅力。 另外我们需要注意的是编译器自动生成的析构函数不会释放自定义类型会因此造成内存泄漏所以在成员变量含有自定义类型的资源需要清理的时候我们必须需要进行自己显式实现析构函数。除非我们实现的类内部自定义成员也是类而且类的内部有自行清理的办法。这种时候我们就可以不用自己实现析构函数 2.3.3拷贝构造 拷贝构造函数就像它的名字一样一般用来拷贝构造它也属于构造函数的一种特殊重载形式是用同类型的对象初始化 拷贝构造函数只有单个形参该形参是对本类类型对象的引用(一般常用const修饰)在用已存在的类类型对象创建新对象时由编译器自动调用。 Date(Date d){_year d._year;_month d._month;_day d._day;} 这里是一般的构造函数的结构注意这里的d必须是引用否则会引发无穷递归 如果不是引用 就会在形参部分会连续构造很多个该类型变量这样的话就会造成无穷递归。 class Date { public:Date(int year 2020, int month 1, int day 1){_year year;_month month;_day day;}Date(Date d){_year d._year;_month d._month;_day d._day;} private:int _year 2020;int _month 1;int _day 1; }; int main() {Date d1(2023, 1, 1);Date d2(d1);return 0; } 上面就是一个拷贝构造函数的实现和使用。 构造函数和其他默认成员函数一样若未显式定义编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按 字节序完成拷贝这种拷贝叫做浅拷贝或者值拷贝。 所以如果我们的自定义类型不是单纯只需要值的时候这种时候我们需要自己实现拷贝构造。比如我们用顺序表实现一个栈我们定义了st1st1中有一个数组当我们拷贝构造给st2st2也会指向这块空间的数组这是很可怕的如果数据的插入和删除会影响数据还算小事那我们的st1和st2其中一个释放空间就是一个很危险的事情了这很有可能会导致程序崩溃所以面对拷贝构造的时候我们需要判断我们是不是只需要浅拷贝再去考虑是不是要自己实现一个拷贝构造。 在实现一个函数传入一个自定义形参和返回一个自定义类型的时候都会发生拷贝构造。 void Func1(Date d) {//... }Date Func2() {Date d;return d; } 比如这里两种都会发生拷贝构造Func1会先拷贝传入的参数在把这个值传入形参中这样实现一次拷贝构造然后这个构造还会再Func1结束之后销毁Func2也差不多Func2中定义了一个d返回d其实并不是返回d本身而是返回d的构造先拷贝构造这个返回值再返回之后再把原来的d销毁。 但是其实我们实现的拷贝构造是有缺陷的我们如果定义一个d1再拷贝构造d2这个d2其实是有更改d1的风险的因为在拷贝构造里面d1传入的是别名这个是可以通过这个更改d1的所以我们应该再修改一下我们的拷贝构造 Date(const Date d){_year d._year;_month d._month;_day d._day;} 加上const让程序更加安全。 2.4运算符重载 2.4.1赋值运算符格式 C为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数也具有其 返回值类型函数名字以及参数列表其返回值类型与参数列表与普通的函数类似。 函数名字为关键字operator后面接需要重载的运算符符号。 函数原型返回值类型 operator操作符(参数列表) 注意 不能通过连接其他符号来创建新的操作符比如operator重载操作符必须有一个类类型参数用于内置类型的运算符其含义不能改变例如内置的整型不 能改变其含义 作为类成员函数重载时其形参看起来比操作数数目少1因为成员函数的第一个参数为隐藏的this.* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现 比如这里我们可以定义一个重载 但是我们发现这里的代码有报错报错内容如下 这是因为它现在在这个类的范围内部我们编译器就把它当做成员函数了在前面我们提过运算符重载函数作为成员函数的时候它会有一个隐藏的参数即这个函数中的this所以我们再传入两个实例就会导致参数过多所以就报错了。怎么解决两个办法 方法一把函数移到外部作为全局函数使用并且直接把类内部的成员开放 // 全局的operator class Date { public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}//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; } 方法二作为成员函数使用并且使用隐藏的参数 class Date { public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}bool operator(const Date d1){return d1._year _year d1._month _month d1._day _day;}private:int _year;int _month;int _day; }; 一般来说我们常用的还是方法二直接定义成成员函数还可以加快调用效率然后我们就可以测试使用了 但是我们发现我们这样写的时候会报错其实本质上其实也是一种操作符重载所以和就是同等级的操作符那么就会从左到右依次执行这样就会产生错误所以我们就要在我们的等式外部包上 int main() {Date d1(2023, 1, 1);Date d2(2023, 1, 2);cout (d1 d2) endl;return 0; } 其他的操作符函数重载也是可以这样使用 2.4.2 赋值运算符重载格式 参数类型const T传递引用可以提高传参效率 返回值类型T返回引用可以提高返回的效率有返回值目的是为了支持连续赋值 检测是否自己给自己赋值 返回*this 要复合连续赋值的含义 Date operator(const Date d){if (this ! d)//如果是同一个变量就不赋值{_year d._year;_month d._month;_day d._day;}return *this;} 像这种普通的日期类不涉及到资源管理我们的赋值运算符重载其实和编译器给的默认拷贝构造没什么区别但是我们的成员变量涉及到资源管理的时候我们就要开始考虑怎么才可以实区域现拷贝但是却又不把两个资源搞混这种时候我们的赋值运算符的实现是很有必要的。 2.4.3前置和后置 在我们使用普通类型的时候有前置和后置两种操作符这种时候我们可以通过的位置来确定它是哪一种但是在我们定义重载函数的时候操作符必须在operator的后面这个时候我们就无法区分两种操作符了这里我们规定前置不作处理后置要在参数列表中给一个int这个int可以不定义任何东西也可以不声明只要有这样一个int在就可以区分前置和后置 // 前置返回1之后的结果// 注意this指向的对象函数结束后不会销毁故以引用方式返回提高效率Date operator(){_day 1;return *this;}// 后置// 前置和后置都是一元运算符为了让前置与后置形成能正确重载// C规定后置重载时多增加一个int类型的参数但调用函数时该参数不用传递编译器自动传递// 注意后置是先使用后1因此需要返回1之前的旧值故需在实现时需要先将this保存一份然后给this1//       而temp是临时对象因此只能以值的方式返回不能返回引用Date operator(int){Date temp(*this);_day 1;return temp;} 2.4.4const成员 在我们的默认的this指向的对象是const修饰的时候我们的传入参数是会报错的 . 比如这里d1在左边d1传入的时候就是当做那个this指向的成员但是一个const修饰之后的是只读的变量传入函数中却变成了可读可写的变量这样造成了权限的放大所以我们需要使用const修饰this才可以解决这个问题这里我们想要修饰隐藏的this只要在函数的后面加上const即可 bool operator(const Date d1)const{return d1._year _year d1._month _month d1._day _day;} 这样this指针就被const修饰了我们的操作符重载就不会导致权限的放大了一般来说我们的所有函数只要不需要更改this指向的变量我们都可以用const修饰一下这样是最安全而且最通用的一种办法。 另外取地址及const取地址操作符重载编译器会自动实现我们不必自行实现。 2.5初始化列表 这里我们再来回顾我们的构造函数在创建对象时编译器通过调用构造函数给对象中各个成员变量一个合适的初始值。 构造函数调用之后对象中已经有了一个初始值但是不能将其称为对对象中成员变量的初始化 构造函数体中的语句只能将其称为赋初值而不能称作初始化。因为初始化只能初始化一次而构造函数体 内可以多次赋值。 这里我们引入初始化列表的概念以一个冒号开始接着是一个以逗号分隔的数据成员列表每个成员变量后面跟一个放在括 号中的初始值或表达式。 class Date { public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}private:int _year;int _month;int _day; }; 可以看到我们使用了初始化列表之后我们不需要在构造函数中再使用之前的赋值模式其实使用初始化列表和之前的区别就是初始化列表类似于带了一个const。每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次) 类中包含以下成员必须放在初始化列表位置进行初始化 引用成员变量        const成员变量        自定义类型成员(且该类没有默认构造函数时) 尽量使用初始化列表初始化因为不管你是否使用初始化列表对于自定义类型成员变量一定会先使 用初始化列表初始化。 另外成员变量在类中声明次序就是其在初始化列表中的初始化顺序与其在初始化列表中的先后次序无关 2.6构造函数的隐式类型转换和explicit关键字 如果一个构造函数接受单个参数并且该参数的类型可以隐式转换为构造函数的参数类型那么这个构造函数可以用于隐式类型转换。这意味着你可以使用一个参数来初始化一个对象而不需要显式调用构造函数。 这种隐式类型可以让我们节省很多的精力比如我们使用栈有push等操作的时候 这里假设我们已经定义了一个stack类类的内部也实现了push操作根据我们以前的经历push需要传入一个实例化的stack我们可能需要写出下面的代码 stack s1(1); stack s2(2); s1.push(); s2.push(); 这样其实是有一点冗杂的我们可以写成这样 stack.push(1); stack.push(2); 这样就简单多了这里的1和2其实进行了隐式类型转换即直接自动传入了s11和s22恶魔可以不需要显式构造了这样其实是方便很多的。 隐式类型转换在只有单参数和多参数有缺省的时候是可以使用的比如 Date(int year):_year(year){} Date(int year, int month 1, int day 1): _year(year), _month(month), _day(day) {} 这里一个就是单参数一个就是全缺省可以只传单参数 。 这种时候我们如果写出下面的代码 Date d1(2022); 用一个整形变量给日期类型对象赋值 实际编译器背后会用2022构造一个无名对象最后用无名对象给d1对象进行赋值.这样效率其实是会一定程度上变低的。 所以有的时候我们可能有禁止使用这种隐式类型转换的需求这种时候我们就可以使用explicit关键字实现禁止隐式类型转换。 explicit Date(int year):_year(year){} explicit Date(int year, int month 1, int day 1): _year(year), _month(month), _day(day){} 2.7static成员 声明为static的类成员称为类的静态成员用static修饰的成员变量称之为静态成员变量用static修饰的成员函数称之为静态成员函数。静态成员变量一定要在类外进行初始化 。 1. 静态成员为所有类对象所共享不属于某个具体的对象存放在静态区 2. 静态成员变量必须在类外定义定义时不添加static关键字类中只是声明 3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问 4. 静态成员函数没有隐藏的this指针不能访问任何非静态成员 5. 静态成员也是类的成员受public、protected、private 访问限定符的限制 static int Count() {static int ret 0;ret;return ret; } class Date { public:static int Count();Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;} private:int _year;int _month;int _day; };int main() {Date::Count();return 0; }比如我们可以这样声明定义并使用一个静态成员函数静态成员函数用的不太多但是在很多时候有很大的用处。  2.8友元 2.8.1友元函数 友元函数friend function是被声明为友元的非成员函数它可以访问类的私有private和保护protected成员。友元函数不是类的成员函数但它可以像类的成员函数一样访问类的内部数据。 一般来说我们的友元函数的声明放在类的内部它的定义放在外部不过在C11之后我们也可以定义在内部这种叫内联友元函数这里不作探讨更多的还是分离的。 我们声明友元函数的位置是没有限制的只要在类的内部即可不过更多的还是声明在最开始的位置我们再在外面定义友元函数并将类的对象作为参数传递这样我们就可以使用友元函数直接使用私有成员了并且我们调用友元函数是全局的我们可以把它当做普通函数调用 void Print(const Date d) {cout d._year - d._month - d._day endl; } class Date {friend void Print(const Date d); public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}private:int _year;int _month;int _day; };int main() {Date d1(2023, 1, 1);Print(d1);return 0; } 这样就很容易访问私有成员了这里的传参也可以是没有const的变量不过我们的函数没有修改这个变量的需求所以加上const修饰更安全一点。 2.8.2友元类 class Date {friend class Time; public:Date(int year 1900, int month 1, int day 1):_year(year),_month(month),_day(day){} private:int _year;int _month;int _day; };class Time { public:Time(int hour 8, int minute 30, int second 30):_hour(hour),_minute(minute),_second(second){} private:int _hour;int _minute;int _second; }; 和友元函数类似这里的友元类就是定义另外一个类B然后在类A中声明友元类B这样我们就可以在B中访问A中的成员了。放在上面的例子就是我们定义了一个Date类又定义了一个Time类我们在Date类中声明了Time是Date的友元这里就可以在Time类中访问Date的成员但是反过来却会有访问限制。 可以打个比较形象的比方我们在Date中声明Time是Date的友元就好像Time在Date中安排了“间谍”间谍可以访问Date中的成员但是Date却不能通过这个间谍访问Time除非Date也在Time中声明一个“间谍”即友元。 特殊的我们在一个类A里面声明另外一个类B那么这个类B就自动成为了类A的友元B可以访问A但是A不能访问B和之前的规则类似只是把友元类的声明换成了类的定义。
http://www.dnsts.com.cn/news/77446.html

相关文章:

  • 吉安网站建设优化服务怎么用wordpress打开网站吗
  • 免费公司网站模板简单html个人网页模板
  • 朝阳区网站开发公司wordpress多大
  • 十大不收费看盘软件网站双重预防机制信息化平台
  • 长春做企业网站多少钱东莞专业网站设计
  • 织梦网站怎样做安全防护哪个网站可兼职做logo
  • app那个网站开发比较好时代汇创网站建设公司
  • 网站建设需要学什么语言微信小程序代码生成器
  • 网站服务器作用潍坊网站排名推广
  • 滁州网站开发公司电话学做网站培训 上海
  • 珠海汽车网站建设wordpress get footer
  • 聚名网域名怎么过户给公司图片网站 seo
  • 出口网站有哪些门户网站的功能
  • 网页与网站设计 什么是属性什么是企业qq什么是营销qq
  • 如何做好外贸网站建设网络推广策划培训班
  • 在线做投资网站南京网站设计公司推荐
  • 好的建网站的书籍外贸网络营销实战
  • 网站怎做百度代码统计北京诚通新新建设有限公司网站
  • 企业查询学历关键词排名优化易下拉排名
  • 网站制作公司排名前十dw网页制作过程
  • 摄影网站开发意义纬天建筑工程信息资讯网
  • 本地电脑做视频网站 外网连接电商网站建设建议
  • 企业的建站方式网站的构建一般要多久
  • 三里屯做网站的公司西安自助网站建设系统
  • 燕郊 网站开发做demo的网站
  • 网站建设要哪些人网站开发代码归属
  • 网站开发的优势app制作视频教程
  • 住建城乡建设部网站哈尔滨seo优化排名
  • 免费发布信息网站有哪些专做兼职的网站
  • 番禺网站排名优化公司免费网站模板库