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

学网站建设宁波品牌网站制作哪家好

学网站建设,宁波品牌网站制作哪家好,班级网站建设的范围,怎么做网站代销类和对象#xff08;下#xff09;再谈构造函数构造函数体赋值初始化列表explicit关键字static成员静态成员的特性友元友元函数友元类成员函数做友元内部类匿名对象编译器的一些优化再谈构造函数 构造函数体赋值 在创建对象的时候编译器会调用构造函数给对象中的成员变量一… 类和对象下再谈构造函数构造函数体赋值初始化列表explicit关键字static成员静态成员的特性友元友元函数友元类成员函数做友元内部类匿名对象编译器的一些优化再谈构造函数 构造函数体赋值 在创建对象的时候编译器会调用构造函数给对象中的成员变量一个合适的初始值 class Date { public: Date(int year, int month, int day) {_year year;_month month;_day day; } private: int _year; int _month; int _day; };虽然在调用构造函数过后对象中的每个成员都有了合适的值但是不能将其称为对对象中的成员变量的初始化构造函数中的语句最多叫赋初值赋值因为初始化只能进行一次赋值可以进行很多次 初始化列表 我现在创建了一个对象Date d1(2022,2,3); 那么在我实例化d1对象的时候_year、_month、_day的空间也随之开好了现在我们要的对这块空间进行初始化啊那么到底在哪里完成对这些成员变量的初始化呢 祖师爷最后最后规定在执行构造函数之前先进行初始化于是他将初始化这个动作放在了构造函数的后面语言上不变描述那个位置我们用图片表示 这块进行初始化操作的区域被称为初始化列表 对于这块区域的写法祖师爷也是有规定的初始化列表以:开头,分隔 每个成员变量后面跟一个放在()里面的初始值或表达式 具体写法如下 当然对于自定义类型子类我们也能让其调用指定的属于它的构造函数来对其进行初始化 那么为啥会有初始化列表勒 主要是因为我们可能会在类的成员变量中定义一些const修饰的成员变量或引用成员变量 我们在实例化出对象过后其成员变量的空间也随之开辟完毕对于普通成员变量我们可以不用初始化但是对于const修饰的成员变量与引用成员变量必须初始化因为这些变量只有一次初始化的机会 未对const修饰的成员变量及引用成员变量进行初始化编译器出现了报错 因此为了解决某些成员变量必须初始化的问题C就提出了初始化列表的操作在初始化列表区间完成成员变量的初始化后来随着C的不断发展在C11中允许在成员变量声明的时候给缺省值这样的作法也是可以的但是在以前的C标准中只有初始化列表才能解决这个问题 注意 1、每个成员变量只能在初始化列表出现一次 2、const修饰的成员便量、引用成员变量、没有默认构造函数的自定义类型必须进行初始化那么为什么没有默认构造函数的自定义类型必须初始化呢因为如果我们没有显示初始化自定义类型的话编译器就会调用默认构造函数来完成初始化但是自定义类型又没得编译器就会报错 3、尽量使用初始化列表因为不管你是否使用初始化列表编译器都会先去初始化列表看一看然后再去成员变量的声明处看看是否有缺省值对于未在初始化列表初始化的成员变量就使用对应函数声明出给的缺省值进行初始化最后才去执行构造函数体 4、在初始化列表编译器会按按照成员变量的声明顺序来初始化与成员变量在初始化列表的位置顺序无关 我们通过一个例子来观察这种现象 允许结果截图 那么为什么会出现这种情况 根据上面的理论aa对象调用了A(int a)构造函数但是编译器不会马上去执行函数体而是先去初始化列表看一看有没有初始化的操作编译器发现的确有于是按照成员变量的声明顺序先对_a2初始化_a2是用_a1初始化的但此时_a1还没完成初始化里面是随机值因此_a2也就被初始化成随机值随后编译按照顺序再完成了_a1的初始化_a1是用a初始化的a是1因此最后我们看到的结果是_a1输出1_a2输出随机值 explicit关键字 对于只有一个参数的或者第一个参数没有给缺省值其余参数都给缺省值的构造函数具有类型转换的作用 我们来看看实例来详细介绍构造函数的类型转换作用 为什么10int可以用来给d1Date初始化 主要是因为int类型向Date类型赋值进行了隐式类型转换10并不是直接赋值给d1的具体作法就是 那么编译器是如何通过10来构造Date类型的临时变量的呢 我的理解是编译器首先实例化好一个Date类型的临时变量然后将10作为Date类型的构造函数的参数来构建临时变量这样就完成了从int类型向Date类型的转换是谁完成这次转换工作的是构造函数因此才说构造函数具有类型转换的作用 但是我们会发现这是不是上面的转换是不是很麻烦又要创建临时变量、又要完成拷贝构造况且我们都能用10来构造临时变量那么我能不能直接用10来构建d1当然是可以的在一些比较新的编译器基本上都采用了这种优化减少了不必要的中间过程提高了程序效率何乐而不为这是编译器最喜欢干的当然比较老的编译器(比如VC6.0)就没有采用上面的优化还是一步一步走的博主用的是VS2022 为此对与Date d110这条语句我们会看到d1会去调用Date(int day)这个构造函数而不会去调用 拷贝构造我们通过运行结果来验证 我们会发现的确实这样当然如果我们想要取消构造函数的的类型转换功能(也就是取消利用其他类型来构建Date对象)我们可以在构建值类型与构造函数类型相匹配的构造函数前面加上explicit关键字来修饰以此来取消该构造函数的类型转换功能比如我们现在不想让利用int类型转换为Date类型我们就可以在Date(int day);构造函数前面加上explicit来修饰就相当于取消了int类型转换为Date类型的“桥梁”如果我们再次使用int类型转换为Date类型编译器就会报错 我们删掉explicit就又可以从int转换为Date类型了 当然我们上面说的是 “对于只有一个参数的或者第一个参数没有给缺省值其余参数都给缺省值的构造函数具有类型转换的作用” 这是C98的标准在C11中具有多个参数的构造函数也具有类型转换的作用也就是说我们可以用{10,20,34}这种方式来构建Date类前提是存在这样的Date(int,int,int)这样的构造函数为此我们可以来实验一把 运行截图 当我们对Date(int year, int month, int day1)构造函数加上explicit关键字修饰时编译器无法完成从{12,13}类型向Date类型及{1,1,2}类型向Date类型的转换编译器会报错 你删掉explicit程序又可以跑起来了 static成员 static成员 用static修饰的成员变量被称为静态成员变量 用static修饰的成员函数被称为静态成员函数静态成员函数没有this指针也就是说对于静态成员函数来说不需要知道是哪个具体的对象在调用它 静态成员(成员变量和成员函数)由所有对象共享不属于某个具体的对象对于静态成员来说不需要知道是那个具体的对象在调用它们因此对于静态成员来说我们既可以通过对象调用也可以通过类域调用 同时静态成员变量不能在初始化列表进行初始化也不能在声明处给缺省值这些地方都是针对非静态成员变量的静态成员变量只能在类外初始化初始化的时候需要指定是那个类域下的静态成员变量 面试题 实现一个类并统计利用该类实例化了多少个对象 分析 我们知道每实例化一个对象必调用构造函数,那么我闷就可以从构造函数内部入手我们可以定义一个全局变量没实例化一个对象就在构造函数内部这个全局变量 代码如下: #includeiostream using namespace std; int g_count 0; class Date { public :Date(){g_count;cout Date() endl;}Date(int year, int month, int day){g_count;cout Date(int year, int month, int day) endl;}Date(const Date d){g_count;cout Date(const Date d) endl;} private:int _year;int _month;int _day; }; void Func(Date d) {Date d1;Date d2(2022, 2, 2);Date d3(d2); } int main() {Date d0;Func(d0);cout g_count endl;return 0; }运行结果 的确实创建了5个对象 这样的代码确实能够得到我们想要的结果但是如果有时候我们不小心对g_count手动了呢 g_count作为全局变量太自由了任意位置都能访问容易造成数据的滥用 为此我们需要限定一下g_count的自由但同时生命周期要是整个程序的 为此我们可以利用static修饰的成员变量来完成最后返回静态变量的值就好了 运行结果 的确也是符合预期的但是现在有个问题就是我们想调用GetCount()函数的话还要专门创建一个对象这是不是就在我们的预期上多了一个就比如原先程序上只有5个对象然后让你统计这5个对象的个数为了统计出这5个对象我们还得创建一个对象用来专门调用GetCount函数因为GetCount函数是动态成员函数需要传递this指针必须通过对象调用所以我们统计到的对象会比原来的多一个既然都知道了多一个我们在输出的时候直接减去不就好了嘛这是在我们知道的情况下假设我们不知道真实情况呢所以我们的代码还需要改进有没有不需要通过对象调用的函数呢? 当然有静态成员函数嘛静态成员函数是不需要传递this指针的也就是说静态成员函数不需要知道是谁调用了它但是它还是一个成员函数属于该类域我们就可以通过类域去调用它因此我们可以将GetCount函数变为静态函数 运行结果 静态成员的特性 1、静态成员不属于某个具体对象为所有对象共享存放在静态区 2、静态成员既可以通过对象访问也可以直接用类域访问 3、静态成员变量必须在类外定义在类内的只是声明并没有在内存中开辟实际空间只有在类外定义过后才会有实际空间才能被使用 4、静态成员函数没有隐藏的this指针因此在静态成员函数内部不能访问非静态成员变量 5、静态成员也是类的成员也受访问限定符的限制 提问 1、静态成员函数可以调用非静态成员函数吗? 答:可以只不过我们需要创建对象然后通过对象来访问非静态成员无法直接访问非静态成员函数因为静态成员函数内部没有this指针调用非静态成员函数是需要传递this指针如果在静态成员函数内部直接访问非静态成员函数的话无法完成this指针的调用编译器会报错 2、非静态成员函数可以调用静态成员函数吗 答:可以调用静态成员函数不需要this指针可以完成传参要求自然也就可以调用静态成员函数同时非静态成员函数有this指针也可以调用其他非静态成员函数 友元 友元提供了一种突破封装的方式有时提供了便利。但是友元会增加耦合度破坏了封装所以 友元不宜多用。 友元分为友元函数和友元类 友元函数 问题现在尝试去重载operator然后发现没办法将operator重载成成员函数。因为cout的 输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作 数了。但是实际使用中cout需要是第一个形参对象才能正常使用。所以要将operator重载成 全局函数。但又会导致类外没办法访问成员此时就需要友元来解决。operator同理。 class Date { public: Date(int year, int month, int day): _year(year), _month(month), _day(day) {} // d1 cout; - d1.operator(d1, cout); 不符合常规调用 // 因为成员函数第一个参数一定是隐藏的this所以d1必须放在的左侧 ostream operator(ostream _cout) {_cout _year - _month - _day endl;return _cout; } private: int _year; int _month; int _day; };友元函数可以直接访问类的私有成员它是定义在类外部的普通函数不属于任何类但需要在类的内部声明声明时需要加friend关键字。 class Date { friend ostream operator(ostream _cout, const Date d); friend istream operator(istream _cin, 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; }; ostream operator(ostream _cout, const Date d) { _cout d._year - d._month - d._day; return _cout; } istream operator(istream _cin, Date d) { _cin d._year; _cin d._month; _cin d._day; return _cin; }总结: 1、友元函数可以访问类的私有、保护、公有成员但不是类的成员函数 2、可以在类内任何地方声明声明的时候需要加上friend关键字修饰 3、友元函数不能加const因为对于成员函数来说加上const并不是用来修饰函数本身的而是修饰*this的友元函数是类外的一个函数不属于成员函数自然不可能拥有this指针this指针都没有const自然无法修饰 4、一个函数可以是多个类的友元函数 5、友元函数与普通函数调用方式一样 友元类 class Time {friend class Date; // 声明日期类为时间类的友元类则在日期类中就直接访问Time类 中的私有成员变量 public: Time(int hour 0, int minute 0, int second 0) : _hour(hour) , _minute(minute) , _second(second) {} private:int _hour;int _minute;int _second; }; class Date { public:Date(int year 1900, int month 1, int day 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour hour;_t._minute minute;_t._second second;} private:int _year;int _month;int _day;Time _t; };总结 1、友元关系是单向的不具有交换性。 比如上述Time类和Date类在Time类中声明Date类为其友元类那么可以在Date类中直接 访问Time类的私有成员变量但想在Time类中访问Date类中私有的成员变量则不行。 2、友元关系不能传递 如果C是B的友元 B是A的友元则不能说明C时A的友元。 3、友元关系不能继承在继承位置再给大家详细介绍。 成员函数做友元 欢迎跳转至我的另一篇博客C核心编程(三) 内部类 概念 如果把一个类定义在另一个类的内部这个类就叫做内部类 内部类是一个独立的类不属于外部类不能通过外部类的对象访问内部类的成员外部类对于内部类来说没有任何访问权限但是反过来在内部类中可以无限制的访问外部类的所有成员简而言之就是内部类是外部类天然的友元类 特性 1、内部类只是在外部类的类域中在利用外部类实例化出来的对象不包含内部类实例化出来的空间 这一点我们可以通过sizeof来验证 利用A类实例化出来的对象大小是4字节而非8字节如果是8字节的话那么就说明A实例化出来的对象的空间包括了B类的对象的空间 2、我们无法直接使用内部类来实例化对象因为内部类的定义是在外部类的作用域下面的不在全局域如果我们直接使用内部类实例化对象的话编译器在全局域找不到内部类的定义会直接报错 因此我们需要显示的告诉编译器内部类的定义所在的作用域让其去外部类的作用域下寻找内部类的作用域 3、内部类的访问也是受外部类的访问限定符的限制如果我们将内部类定义在外部类的protected、private区域我们在外部类的外部是无法通过外部类::内部类 对象名这样的方式实例化内部类编译器会告诉我们不可访问 4、在内部类内部可以直接访问外部类的静态成员可以不需要通过类名::或对象.的方式调用 匿名对象 顾名思义就是没有名字的对象 class A { public:A():_a(0){cout A() endl;}A(int a) :_a(a){cout A(int a) endl;}A(const A a):_a(a._a){cout A(const A a) endl;}~A(){cout ~A() endl;}void test(){cout void test() endl;} private:int _a; }; int main() { //匿名对象的生命周期只有所在行离开所在行就会被析构掉A();//这是一个匿名对象A(1);//这也是一个匿名对象A(A(1));//这也是一个匿名对象return 0; }编译器的一些优化 在传参和传返回值的过程中一般编译器会做一些优化减少对象的拷贝这个在一些场景下还 是非常有用的。 演示代码 class A { public:A():_a(0){cout A() endl;}A(int a) :_a(a){cout A(int a) endl;}A(const A a):_a(a._a){cout A(const A a) endl;}~A(){cout ~A() endl;}void test(){cout void test() endl;}A operator(const A a){cout A operator(const A a)endl;_a a._a;return *this;} private:int _a; }; void fun1(A a) {} A fun2() {A a;return a; }首先我们来看第一组 正常情况下aa1调用A()构造函数调用fun1然后aa1传参给形参形参会调用拷贝构造函数 最后程序结束 我们来看看结果是不是这样 这个没什么可说的也没有可以优化的地方是正常情况 我们再来看看第二组 首先程序先去调用fun2来到fun2内部调用A构造函数初始化a对象然后返回a对象 一般情况下对于这种值返回的函数并不是直接将值返回而是按照下面这种方式 那么综上所述编译器先调A()构造函数初始化a对象然后再调用的拷贝构造函数初始化的临时对象 通过程序验证一遍 我们会发现编译器并没有调用拷贝构造函数这是为什么呢 主要就是因为编译嫌麻烦你看啊编译器都可以用返回值来初始化临时变量的那么为啥不直接用返回值来构造接受对象呢 也就是说在编译器看来一个表达式中连续构造拷贝构造-优化为一个构造编译器先麻烦对其进行了优化省去了中间商赚差价(临时变量消耗时间)有利于提高程序运行效率 为此我们没有看到拷贝构造函数 第三组 这是int类型传给A类型会出现隐式类型转换一般情况如下 也就是说在一般情况下我们会看到编译器先调用A(int)构造函数再调用拷贝构造函数 但是这么绕圈的方式太麻烦了编译想一步到位根据 “一个表达式中连续构造拷贝构造-优化为一个构造”的规则编译器就直接拿着这个1去构造形参减少了中间商赚差价提高了程序运行效率 为此经过优化过后编译器只会去调用A(int)构造函数来实现(int)到(A)的类型转换 第四组 一般情况 根据“一个表达式中连续构造拷贝构造-优化为一个构造”的规则 编译器会直接拿着1去构建形参对象a因此编译器只会调用Aint构造函数来完成此次类型转换不会生成匿名对象 运行结果 第五组 我们来仔细推敲一下 也就是说从return语句开始调用临时对象的拷贝构造函数、调用aa1的拷贝构造函数这样的连续拷贝让编译器受不了了编译器对其进行了优化直接直接绕过临时变量这个中间商直接调用aa1的拷贝构造函数将a对象的值拷贝给aa1 为此我们最后看到的是编译器只会调用A()构造函数初始化a对象、拷贝构造函数(初始化aa1); 对于一些激进的编译器会将对象a的构造函数也优化了直接拿初始化a的值来初始化aa1 为此我们看到的就只有编译器调用A()这种比较激进的结果在VS2022下可以看到 一般来说编译器是不会将A a调用构造函数也加入优化因为和reture语句都不是同一行贸然的优化进来会出现风险和bug! 我们拿比较稳妥的结果总结出一套优化结论 一个表达式中连续拷贝构造拷贝构造-优化一个拷贝构造 第六组 在比较稳定的编译器不激进的编译器下这个过程是无法优化的因为既满足一个表达式中连续拷贝构造拷贝构造-优化一个拷贝构造的规则也不满足“一个表达式中连续构造拷贝构造-优化为一个构造”的规则编译器只能老老实实的按照原样走我们就会看到编译器调用A()、A()、A(const A)、A operator(const A a)这些函数 但是对于一些比较激进的编译器他们会优化掉拷贝给临时对象的过程直接拿返回值赋值给接受对象VS2022下就是这样做的 总结 1、为了提高程序运行效率参数传递尽量用引用尽管使用值传递的方式大多有优化但是都需要去调用构造函数会比较浪费时间使用引用的话可以避免拷贝、调用构造函数 2、尽量使用定义对象的方式接受值传递的返回值避免使用赋值的方式接收值传递的返回值通过对比第五组、第六组函数调用明显使用第五组的方式接受返回值的效率更高(我们讨论的是在不激进的编译器下的优化) 3、尽量使用匿名对象充当返回值
http://www.yayakq.cn/news/1453/

相关文章:

  • 做互联网产品和运营必备的网站南京高端网站建设
  • 江西锦宇建设集团有限公司网站小程序如何推广
  • 在线制作手机网站mukioplayerwp wordpress
  • wordpress设置多站点微信链接制作软件
  • 网站建设和网站设计一样吗甘肃省住房和城乡建设厅网站职称证查询
  • 鞍山网站制作一般多少钱wordpress多国语言设置
  • 互联网网站解决方案私密浏览器免费版图片
  • 网站代理备案步骤网站设计公司杭州
  • wordpress 网站积分打赏找做牙工作上哪个网站
  • 网站论坛建设网络营销资格证网络营销证书
  • 世纪佳缘网站开发公司建设部二级结构工程师注销网站
  • 中国乐清做网站需要找人优化吗
  • 网站会员注册系统源码北京智能模板建站
  • 山西做网站运营的公司网站没更新
  • 学ps做兼职的网站有哪些做网站设计赚钱吗
  • 美食网站开发步骤如何百度搜索到自己的网站
  • iis 多网站安全设置淘宝详情页模板
  • asp.net搭建网站搭建棋牌工具
  • 文库网站开发建设seochinaz查询
  • 国外做的好的医疗网站阿里云wordpress root
  • 现在网站建设用到哪些技术wordpress右侧悬浮搜索菜单
  • 容县网站开发上海装修网站大全
  • 微企免费网站建设微信h5的制作方法
  • 免得做网站智能建设网站
  • 循化网站建设公司济宁建设工程信息网站
  • 网站建设及 维护电子产品外贸交易平台
  • 微网站免费开发平台手机优化助手怎么删除
  • 北京网站建设公司司文创产品设计作品欣赏
  • 网站撤销备案个人怎么注册公司需要多少钱
  • 网站专题页面设计欣赏wordpress网站如何app