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

网站建设教程asp佛山网站推广优化公司

网站建设教程asp,佛山网站推广优化公司,hexo ghost wordpress,处理事件seo软件目录 介绍类和对象 一. 类和对象——类的定义 1.访问限定符 2.类域 作用操作符:: 3.对象大小 类的实例化 内存对齐规则 4.this指针 this指针会出现的问题 5.C语言结构体与C类对比 封装的本质 C类的优点 二 .类和对象——关于成员 1.类的默认成员函数 I.构造函数 构… 目录 介绍类和对象 一. 类和对象——类的定义 1.访问限定符 2.类域 作用操作符:: 3.对象大小 类的实例化 内存对齐规则 4.this指针 this指针会出现的问题 5.C语言结构体与C类对比 封装的本质 C类的优点 二 .类和对象——关于成员 1.类的默认成员函数 I.构造函数 构造函数——初始化列表 初始化列表注意事项 II.析构函数 III.拷贝构造函数 IV.赋值运算符重载 V.取地址运算符重载普通对象 VI.取地址运算符重载const对象 const成员函数 类的默认成员函数总结 2.static成员 3.友元和内部类 友元函数 友元类和内部类 4.匿名对象 5.类型转换与对象拷贝时的编译器的优化 介绍类和对象 类是对象一种抽象的描述是定义同一类所有对象的变量和方法的蓝图或模型限定了类有哪些成员定义出一个类并没有分配实际的内存空间来存储它因为这些成员变量只是声明只有在实例化出对象的时候才会分配空间。 所以用类类型在物理内存中创建对象的过程就是类实例化出对象。一个类可以实例化出多个对象实例化出的对象会占用实际的物理空间用来存储类成员变量 类和对象的关系是类是对象的抽象而对象是类的具体实例。‌ 一. 类和对象——类的定义 C语言中我们定义一个结构体是用的struct //定义一个日期类型 struct Date {int year;int month;int day; }; C是兼容C语言的也可以用struct定义同时struct升级成了类C中还有一个定义类的关键字是class如下所示 class Date {//类的成员 public://成员函数 //后面会补充构造和析构函数Date() //默认构造构造函数{}Date(const Date d) //拷贝构造构造函数{}~Date() //析构函数{}private: //成员变量或叫类的属性int _year;int _month;int _day; }; //这个分号不能省略 在{}中的为类的主体与struct一样不能省去结束时的分号类里面的内容是类的成员类中的函数叫做类的成员函数或类的方法C中struct里也可也定义函数定义在类中的函数默认是内联inline,类中的变量可以称为成员变量或类的属性。 1.访问限定符 从上面两个代码相比看出C定义类多出了两个关键字public和private这两个是访问限定符访问限定符有三个public公有修饰的成员在类外可以直接被访问、private私有、protected保护private和protected修饰的成员都不能直接在类外被访问。 访问权限作用域从该访问限定符的出现的位置开始直到下一个访问限定符出现为止若后面没有访问限定符就到 } 结束即类结束 class和struct都可以定义类但是它们之间也是有区别的struct定义的成员默认都是public而class默认的都是private一般用class定义类较多。 2.类域 类定义了一个新的作用域类的所有成员都在类的作用域中所以在类外定义成员的时候需要使用作用域操作符::来指明成员属于哪个类域。 作用操作符:: 用来指定访问某一空间或作用域中的成员的 使用方式如下 //命名一个叫FFDUST的空间 namespace FFDUST {//可以在这个里定义变量/函数/类型int a 1;void func(){cout func() endl;}struct Date{int year;int month;int day;}; }int main() {//作用域限定符就可以访问指定在某一空间的成员//指定FFDUST命名空间里的变量acout FFDUST::a endl;//指定FFDUST命名空间里的func函数FFDUST::func();//用指定FFDUST命名空间里的Date类型定义一个变量aFFDUST::Date a;cout sizeof(a) endl;return 0; } 还可以用于命名空间嵌套时候 //命名一个叫FFDUST的空间 namespace FFDUST {//命名一个叫FF的空间namespace FF{int FF 10;int Add(int x, int y){return x y;}}//命名一个叫DUST的空间namespace DUST{int DUST 1;int Add(int x, int y){return (x y) * 100;}} }int main() {cout FFDUST::FF::FF endl;cout FFDUST::DUST::DUST endl;cout FFDUST::FF::Add(1, 1) endl;cout FFDUST::DUST::Add(1, 1) endl;return 0; } 类域影响的是编译的查找规则若定义了一个类类的成员的声明和定义分离如成员函数就需要指定类域 否则编译器会认为全局的或者当前某个命名空间的编译时就导致找不到成员的声明/定义在哪 3.对象大小 类的实例化 用类类型在物理内存中创建对象的过程称为类实例化出对象。一个类可以实例化出多个对象实例化出的对象占用实际的物理空间来存储类成员变量。 只有用类实例化出对象时才会分配空间没有实例化时这些成员变量只是声明不会分配空间。 如何实例化如下测试  //简单定义一个日期类测试 class DateTest { public://默认构造Date(int year 1900, int month 1, int day 1):_year(year),_month(month),_day(day){cout Date() endl;}//拷贝构造Date(const Date d) :_year(d._year), _month(d._month), _day(d._month){cout Date(const Date d) endl;}//析构~Date(){cout ~Date() endl;}void Print(){cout _year / _month / _day endl;}private:int _year;int _month;int _day; };int main() {//这里就是实例化出了d1,d2,d3DateTest d1;DateTest d2(2024, 10, 12);DateTest d3(d1);d1.Print();d2.Print();d3.Print();cout sizeof(d1) endl sizeof(d2) endl sizeof(d3) endl;return 0; } 我们看一下输出结果 实例化之后就可以来分析对象的大小了C中也有内存对齐规则与C语言结构体一致 内存对齐规则 第一个成员在与结构体偏移量为0的地址处。其他成员变量要对齐到某个数字对齐数的整数倍的地址处。对齐数编译器默认的一个对齐数与该成员大小的较小值vs下默认的对齐数8结构体总大小为最大对齐数所有变量类型最大者与默认对齐参数取最小的整数倍。如果嵌套了结构体的情况嵌套的结构体对齐到自己的最大对齐数的整数倍处结构体的整体大小就是所有最大对齐数含嵌套结构体的对齐数的整数倍。 拿上述测试程序来解释 不过C类中有特殊情况 空类和只有成员函数的类的大小该是什么呢 class A { public:void Print(){//...} }; class B {}; int main() {A a;B b;cout sizeof(a) endl;cout sizeof(b) endl;return 0; } 它们大小结果是1这里给1个字节是为了占位标识对象的存在若一个字节都不给无法表示对象存在过。 4.this指针 this指针就是当前类类型的指针编译器编译后类的成员函数默认都会放在形参第一个位置它的作用就是类的成员函数中访问成员变量的本质都是通过this指针访问的。 注意C规定不能在实参和形参的位置显示的写this指针编译时编译器会处理但可以在函数体内显示使用this指针。 class Date { public://默认构造Date(int year 1900, int month 1, int day 1):_year(year),_month(month),_day(day){}//拷贝构造Date(const Date d) //本质是 Date(Date* const this , const Date d) 但this这里不能显示写 {//这里可以显示写thisthis-_year year; //_yearyear;this-_month month; //_month month;this-_day day; //_day day;}~Date(){}void Print() // void Print(Date* const this{cout _year / _month / _day endl;}private:int _year;int _month;int _day; }; 注这里const在*之后修饰的是指针本身若const在*之前就相当于const (*this) 修饰的是指针指向的内容。 this指针会出现的问题 this指针的类型:类类型* const所以不能给this赋值并且只能在该类的成员函数的内部使用。this指针本质上是成员函数的形参当对象调用成员函数时对象将地址作为实参传递给this形参所以对象中不存储this指针形参存在栈帧里所以this指针默认存在栈而不是静态区。this指针一般由编译器通过ecx寄存器自动传递不需要用户传递。 就比如下代码这种情况不会报错 #includeiostream using namespace std; class A { public:void Print(){cout A::Print() endl;} private:int _a; };int main() {A* p nullptr;p-Print();return 0; }并且可以正常运行 调试监视窗口来看这个p就是个空 但这里p-调用本质上不是解引用操作是通过底层汇编上跳转至那个函数 但下面这种情况就是对空解引用会崩溃 #includeiostream using namespace std; class A { public:void Print(){cout _a endl; //这里就相当于this-_a_a是存到对象里的这就会导致对空指针解引用} private:int _a; };int main() {A* p nullptr;p-Print();return 0; }5.C语言结构体与C类对比 封装的本质 封装将数据和操作数据的方法进行有机结合隐藏对象的属性和实现细节仅对外公开接口来 和对象进行交互。 封装是一种更严格规范的管理避免出现乱访问修改的问题也让用户更方便使用类。 C的类就是封装的一种体现C的类中不仅有数据还有成员函数通过类将数据以及操作数据的方法进行有机结合通过访问权限来隐藏对象内部实现细节控制哪些方法可以在类外部直接被使用。 C类的优点 C语法相对C语言的方便比如给函数参数缺省值也可以完成初始化并且成员函数的参数中隐含this指针就不需要每次传对象的地址还可以直接使用类名当类型不需要typedef。 class A { private:int _a; };struct B {int b; }; typedef struct B B;int main() {A a; //C类名可以直接当类型struct B b1; //C语言需要这样写或者用typedefB b2;return 0; } 二 .类和对象——关于成员 1.类的默认成员函数 类的默认成员函数就是用户没有显示实现的编译器会自动生成的成员函数。 编译器会默认生成6个默认成员函数 I.构造函数 构造函数主要任务就是对象实例化时初始化对象会自动调用。注意构造函数虽叫构造但主要并不是开空间创建对象我们常用2的局部对象是栈帧创建时空间就开好了。 构造函数的特点 函数名与类名相同无返回值啥也不要写甚至也不写void对象实例化时系统会自动调用对应的构造函数构造函数可以重载如果类中没有显示定义构造函数C编译器会自动生成一个无参的默认构造函数一旦用户显示定义编译器就不再生成默认构造函数就是不传实参的构造函数有无参构造函数、全缺省参数的构造函数、不写构造时编译器默认生成的构造函数。注意三个函数只能存在一个不能同时存在无参构造函数和全缺省参数构造函数可以构成函数重载但是当不传参数的时候二者会存在歧义不显示实现编译器默认生成的构造对内置类型成员变量的初始化是没有要求的也就是说取决于编译器有些编译器会给它们初始化对于自定义类型成员变量要求调用这个成员变量默认构造函数初始化比如用两个stack实现一个queue前提是stack要有默认构造queue就可以不用写默认调用它的成员变量stack里的如果这个成员变量没有默认构造就会报错。可以用初始化列表初始化成员变量。 class Date { public://默认构造 -- 无参构造函数Date(){_year 1900;_month 1;_day 1;}//带参构造 --- 用于传参的Date(int year, int month, int day){_year year;_month month;_day day;}//注意全缺省构造函数与无参构造函数不能同时存在这里写一起是为了展示它们//默认构造 -- 全缺省构造函数Date(int year 1900, int month 1, int day 1):_year(year) //初始化列表写法, _month(month), _day(day){}//默认构造 -- 全缺省构造函数Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}private:int _year;int _month;int _day; };注意构造函数是一定要写的因为我们要定义这个类的初始化方式。 构造函数——初始化列表 我们上面介绍构造函数初始化还可以用初始化列表它的使用方式以:开始接着是一个以逗号分隔的数据成员列表每个成员变量后面跟一个放在括号中的初始值或表达式。 初始化列表是每个成员变量定义初始化的地方 不在初始化列表初始化的成员也会走初始化列表所以尽量使用初始化列表初始化每个成员变量在初始化列表中只能出现一次。引用成员变量、const成员变量和没有默认构造的类类型变量是必须要在初始化列表位置进行初始化否则会报错。 也可以写成下面形式 class Date { public://默认构造Date(){}//带参构造 --- 用于传参的Date(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout _year / _month / _day endl;}private://也可以在这里给缺省值成员变量声明位置给缺省值//这里也是给初始化列表初始化成员用的//函数参数的缺省值给显示传不传参用的int _year 1900;int _month 1;int _day 1; };int main() {Date d1;d1.Print();return 0; } 注意 初始化列表中是按照成员变量在类中声明的顺序进行初始化跟成员在初始化列表出现的先后顺序无关建议是声明顺序和定义顺序一致。 class test { public:test(int a):_a1(a),_a2(_a1){}void Print(){cout _a1 _a2 endl;} private:int _a2 1;int _a1 1; };int main() {test a(10);a.Print();return 0; } 可以看到输出_a2时是一个随机值这就是因为_a2比_a1先声明_a2就先用_a1初始化 但是这时_a1并没有被10初始化是一个随机值就导致了上面的情况。 初始化列表注意事项 无论是否显示写初始化列表每个构造函数默认构造函数也有函数参数缺省值在初始化列表走的都有初始化列表无论是否在初始化列表显示初始化每个成员变量都要走初始化列表初始化 每个成员变量都要走初始化列表的情况总结 1. 在初始化列表初始化的成员显示写的 2. 没有在初始化列表的成员不显示写          a. 声明的地方有缺省值就用缺省值          b. 没有缺省值                     x:内置类型不确定是否初始化取决于编译器                     y:自定义类型调用默认构造没有默认构造就报错  3. const、引用、没有默认构造的自定义类型必须在初始化列表初始化 II.析构函数 与构造函数相反C规定对象在销毁时会自动调用析构函数完成对象中资源的清理释放工作。 注意析构函数不是完成对对象本身的销毁比如局部对象是存在栈帧的 函数结束栈帧销毁他就释放了不需要管。 析构函数的特点 析构函数名是在类名前加上字符~无参数无返回值。与构造类似一个类只能有一个析构函数。若未显示定义系统会自动生成默认的析构函数对象生命周期结束时会自动调用析构函数若不显示写编译器自动生成的析构函数对内置类型成员不做处理自定义类型成员会调用它的析构函数与构造类似自定义类型成员无论声明情况都会自动调用析构函数(当显示写析构函数时对于自定义类型成员也会调用它的析构)C规定一个局部域的多个对象后定义的先析构相当于栈--后进先出与构造类似若类中没有申请资源析构函数可以不用写直接使用编译器生成的默认析构函数我们实现的Date类成员变量类型是内置类型并且没有空间资源的申请就可以不用写。若默认生成的析构可以用也不需要显示写析构如自定义类类型成员调用它的析构两个Stack实现一个MyQueue前提是Stack要有显示实现的析构因为它有资源的申请否则会造成资源泄露 class Date { public://默认构造Date(int year 1900, int month 1, int day 1):_year(year),_month(month),_day(day){cout Date() endl;}//析构 用于清理释放资源~Date(){cout ~Date() endl;}private:int _year;int _month;int _day; };int main() {Date d1;return 0; } 这里可以不用显示写析构 上面介绍了我们看一下要显示写析构的情况 class Stack { public://默认构造Stack(int n 4){_a new int[n] {0}; //申请n个连续空间并都初始化为0//new可以不用显示写检测异常若出现异常自己会抛异常_capacity n;_top 0;cout Stack(int n 4) endl;}//析构函数 这里要显示写因为上面构造时候申请了空间~Stack(){delete[] _a; //释放_a中n个连续空间_a nullptr; //C中nullptr代表空指针_top _capacity 0;cout ~Stack() endl;} private:int* _a;size_t _capacity;size_t _top; };int main() {Stack s1;return 0; } 这里是必须要写的否则会造成资源泄露。 III.拷贝构造函数 拷贝构造函数是一种特殊的构造函数通俗来说是想用自己的类型的对象来初始化自己 拷贝构造函数它的第一个参数是自身类类型的引用且任何额外的参数必须都要有默认值或无额外参数这个用的最多 C规定自定义类型对象进行拷贝行为必须调用拷贝构造所以这里自定义类型传值传参和传值返回都会调用拷贝构造完成。 拷贝构造写法 class Date { public://默认构造Date(){}//构造函数Date(int year, int month, int day){_year year;_month month;_day day;}//拷贝构造函数////参数类型必须是自身类类型的引用Date(const Date d) //const是为了保护形参不被改变{_year d._year;_month d._month;_day d._day;}//析构函数~Date(){}private:int _year 1900;int _month 1;int _day 1; }; 拷贝构造函数特点 拷贝构造函数与构造函数构成重载拷贝构造第一个参数必须是自身类类型对象的引用使用传值会报错会引发无穷递归。拷贝构造也可以多个参数但第一个参数必须是类类型对象的引用后面的参数必须有缺省值。若不显示定义拷贝构造编译器会自动生成拷贝构造函数浅拷贝会对内置类型成员变量完成值拷贝/浅拷贝一个字节一个字节的拷贝类似于memcpy所以日期类这种可以不用写。对自定义类型成员会调用它的拷贝构造。像日期类成员变量都是内置类型且没有指向什么资源的编译器自动生成的拷贝构造函数就可以完成需要的拷贝所以可以不用显示实现拷贝构造。若指向了资源如Stack虽然都是内置类型但它的_a成员指向了资源就需要自己实现深拷贝对指向的资源也进行拷贝浅拷贝只会让它们指向同一块资源。若类内部成员有自定义类型成员编译器自动生成的拷贝构造就会调用这个自定义类型成员的拷贝构造也不需要实现比如两个Stack实现一个queueStack前提要有拷贝构造这样就会自动调用Stack的拷贝构造传值返回引用会产生一个临时对象调用拷贝构造。传引用返回返回的是返回对象的别名引用没有产生拷贝不会调用拷贝构造因为没有产生临时对象返回的就是它本身。注意若返回对象是一个当前函数局部域的局部对象函数结束就销毁了这时候使用引用返回时有问题的相当于野引用类似野指针。所以要保证返回对象在当前函数结束还在来使用引用。 拿栈来举例对比一下浅拷贝深拷贝  class Stack { public://默认构造Stack(int n 4){_a new int[n] {0};_capacity n;_top 0;}//拷贝构造Stack(const Stack s){_a new int[s.capcaity()] {0};memcpy(_a, s._a, sizeof(int) * s.top());_capacity s.capcaity();_top s.top();}~Stack(){delete[] _a;_a nullptr;_top _capacity 0;}void Push(int x){if (_top _capacity){int newcapacity _capacity * 2;int* tmp (int*)realloc(_a, newcapacity *sizeof(int));if (tmp NULL){perror(realloc fail);return;}_a tmp;_capacity newcapacity;}_a[_top] x;}size_t top()const{return _top;}size_t capcaity()const{return _capacity;}private:int* _a;size_t _capacity;size_t _top; }; int main() {Stack s1;s1.Push(1);s1.Push(2);Stack s2(s1);//拷贝构造也可以写成//Stack s2 s1;return 0; } 可以看到它们两个指向不同空间但数据相同 若是浅拷贝可以看出它们指向同一块空间并且程序会报错。 IV.赋值运算符重载 赋值运算符重载也是一个默认成员函数用于两个已经存在的对象直接的拷贝赋值拷贝构造是用一个存在的对象去初始化另一个要创建的对象 Date d1(2024,10,1); Date d2(d1); //拷贝构造Date d3(2024,10,2); Date d4(2024,10,1); d3d4; //赋值运算符重载赋值运算符重载特点与拷贝构造类似 赋值运算符的特点  是一个运算符重载并且要必须重载为成员函数为了减少拷贝且不想对象被修改参数建议写成const当前类类型与拷贝构造一样它有返回值目的是为了支持连续赋值的场景返回类型建议写成当前类类型的引用引用返回可以提高效率不会产生临时对象减少拷贝返回的就是对象的别名就是它本身,可以直接修改。没有显示实现时编译器会自动生成一个默认赋值运算符重载它与拷贝构造类似对内置成员变量完成浅拷贝对自定义类型成员变量会调用它的赋值运算符重载。与拷贝构造类似像Date类的成员变量全是内置类型且没有指向什么资源编译器自动生成的赋值运算符重载就够用了所以可以不显示实现。若像stack类虽然都是内置类型但它指向了资源情况与拷贝构造类似就需要实现深拷贝。若类型内部主要是自定义类型成员编译器自动生成的赋值重载就会自动调用自定义类型成员的赋值运算符重载也可以不用显示实现如两个stack实现一个queue。 结合上面的就可以把日期类主要用的最多的默认成员函数实现了  class Date { public://构造函数可以传参构造不传参就是默认构造Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}//拷贝构造函数Date(const Date d){_year d._year;_month d._month;_day d._day;}//赋值运算符重载 --- d1 d2Date operator(const Date d){_year d._year;_month d._month;_day d._day;//cout Date operator(const Date d) endl;//d1 d2 -- d1.operator(d2) 表达式返回的对象应该是d1本身所以要对this指针解引用。// 用引用返回*this就是d1的别名 return *this; //this指针可以在这里显示调用指向当前成员}//析构函数内置类型也不用显示写编译器会自动生成且Date类没有申请资源~Date(){}void Print(){cout _year / _month / _day endl;}private:int _year;int _month;int _day;}; // int main() {Date d1(2024, 10, 14);Date d2(1900, 1, 2);Date d3(d1); //拷贝构造d1.Print();d2.Print();d3.Print();d1 d2; //赋值运算符重载cout endl;//d1 d2 -- d1.operator(d2) d1.operator(d2);d1.Print();d2.Print();d3.Print();cout endl;//有返回值就可以连续赋值d1 d2 d3;d1.Print();d2.Print();d3.Print();return 0; } 输出结果 V.取地址运算符重载普通对象 取地址运算符重载普通对象和const对象用的都比较少一般编译器这两个函数自动生成就够用了不需要显示实现除非特殊场景不想让别人取到当前类对象的正确地址可以实现一份返回一个假地址。 class Date { public://构造函数可以传参构造不传参就是默认构造Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}//拷贝构造函数Date(const Date d){_year d._year;_month d._month;_day d._day;}//赋值运算符重载 --- d1 d2Date operator(const Date d){_year d._year;_month d._month;_day d._day;//cout Date operator(const Date d) endl;//d1 d2 -- d1.operator(d2) 表达式返回的对象应该是d1本身所以要对this指针解引用。// 用引用返回*this就是d1的别名 return *this; //this指针可以在这里显示调用指向当前成员}//析构函数内置类型也不用显示写编译器会自动生成且Date类没有申请资源~Date(){}void Print()const{cout _year / _month / _day endl;}Date* operator(){//return this; //this指针指向该对象这样是返回正确的地址//返回空//return nullptr;//返回一个乱输的地址return (Date*)0x1546652; //两者目的都一样}const Date* operator()const //const对象要调用const成员函数且返回const Date*{//return this; //this指针指向该对象这样是返回正确的地址//return nullptr;return (Date*)0x1546600;}private:int _year;int _month;int _day; }; int main() {Date d1(2024, 10, 14);const Date d2(1900, 1, 2);cout d1 endl;cout d2 endl;return 0; } 可以看出实际地址与打印的地址明显不同这里const成员和普通成员的取地址运算符重载函数也构成了函数重载。 自定义类型用运算符都需要重载来自定义它的行为系统不提供除赋值重载和取地址重载外 VI.取地址运算符重载const对象 const对象取地址运算符重载与上面类似就是需要强调的是const成员函数 const成员函数 就是const修饰的成员函数注意const修饰的成员函数要放到成员函数参数列表之后 const修饰该成员函数隐含的this指针就拿上面Print成员函数Print隐含的this指针实质就是 const Date* const this;  //第一个const修饰指针指向的内容的 往后不想修改成员变量的建议加const。  const修饰的对象是需要调用const修饰的成员函数的不能调用普通成员函数普通对象可以调用普通成员函数和const修饰的成员函数。 类的默认成员函数总结 一般情况下类的构造是需要自己写的因为要自己去定义类的初始化方式拷贝构造、赋值重载、析构是不需要写的编译器会自动生成调用若需要深拷贝的情况这些则都需要自己实现。 如果一个类显示实现了析构并释放资源那么他就需要显示写拷贝构造否则不需要。 如果一个类显示实现了析构并释放资源那么他就需要显示写赋值运算符重载否则不需要。  2.static成员 static修饰的成员变量是静态成员变量它是必须要在类外初始化。static修饰的成员函数是静态成员函数它没有this指针。静态成员也是类的成员也受访问限定符限制。静态成员函数中可以访问其他静态成员但不能访问非静态成员非静态成员含this指针非静态成员函数是可以访问任意静态成员变量和静态成员函数的。突破类域就可以访问静态成员可以通过 类名::静态成员静态成员可以通过类名直接访问而不需要创建类的实例。 或者 对象.静态成员 来访问静态成员变量 和静态成员函数不过前提是要在公有的情况下。 class A { public:A(){_count;}A(const A t){_count;}~A(){--_count;}static int GetAcount() //没有默认自带this指针{return _count;} private:static int _count; //要在类里面声明//注意不能给缺省值因为它不是存在静态区中的不会走初始化列表 };//在类外面初始化 int A::_count 0; //声明已经有static修饰了定义就不需要加了int func() {static int _n 0;return (_n); }int main() {cout A::GetAcount() endl; //类名::静态成员函数A a1, a2;A a3(a1);cout A::GetAcount() endl;cout a1.GetAcount() endl; //对象.静态成员函数cout endl;cout func() endl;cout func() endl;cout func() endl;//这里不能直接访问_count因为它被private修饰了。return 0; } 从输出结果看出static修饰的成员变量与static修饰局部变量都可以被保存。 所以静态成员变量为所有类对象所共有它不属于某个具体的对象不存在对象中而存在静态区中在内存中只有一份拷贝被类的所有实例共享。 静态区存储全局变量和静态变量的。 静态成员变量不存在对象中它就不会走构造函数初始化列表所以静态成员变量不能在声明的位置给缺省值初始化缺省值是为了构造函数走初始化列表的。 如下例题 //设已经有A,B,C,D 4个类的定义程序中A,B,C,D构造函数调⽤顺序为 //设已经有A,B,C,D 4个类的定义程序中A,B,C,D析构函数调⽤顺序为 class A { public:A(){cout A() endl;}~A(){cout ~A() endl;} private:int _a; };class B { public:B(){cout B() endl;}~B(){cout ~B() endl;} private:int _b; };class C { public:C(){cout C() endl;}~C(){cout ~C() endl;} private:int _c; };class D { public:D(){cout D() endl;}~D(){cout ~D() endl;} private:int _d; }; C c; //全局变量在main函数建立前就创建了int main() {A a;B b;static D d; /*静态无论是内置类型或自定义类型是在第一次走到定义的这个地方时才会初始化只有全局的静态才会在main函数建立之前初始化。*///d声明周期时全局的且作用域时局部的所以会先析构D再Creturn 0;//析构是后进的先析构 }输出结果 3.友元和内部类 ‌friend是友元的关键字友元分为两类友元函数和友元类用的时候需要在函数声明或者类声明前面加friend它提供了一种突破类访问限定符封装的方式。 友元函数 外部友元函数可以访问到类的私有和保护成员需要访问类的私有会涉及到友元友元函数是一种声明不是类的成员函数。 它也可以是多个类的友元函数。 //要注意在这里先声明B类因为编译器是向上找的 //否则A的友元函数声明时编译器不认识B class B;//A和B都有func的友元声明func是它们俩类的友元函数 class A {friend void func(const A a, const B b); private:int _a 100; };class B {friend void func(const A a, const B b); private:int _b 120; };void func(const A a, const B b) {cout a._a endl; //在类中友元声明func就可以访问它们俩的私有了cout b._b endl; }int main() {A aa;B bb;func(aa, bb);return 0; } 可以看到A和B的私有成员变量被访问到了。 友元类和内部类 内部类默认是它外部类的友元类它具有友元类的特点内部类本质是一种封装它是一个类定义到另一个类的内部但它是一个独立的类外部类定义的对象中不包含内部类它只受外部类类域限制和访问限定符限制的。 友元类的特点 友元类中的成员函数都可以是另一个类的友元函数也都能访问那个类私有和保护成员。友元类的关系是单向的A是B的友元但B不是A的友元。友元类的关系不能传递A是B的友元B是C的友元但A不是C的友元。 //内部类class A { public:class B //B是A的友元类不是A的成员{public:void BprintA(const A a){cout a._a1 endl;cout a._a2 endl;}private:int _b1 120;int _b2 12;};private:int _a1 100;int _a2 10; };int main() {cout sizeof(A) endl; A aa;//这样定义B是不行的B是在A这个类的类域里//B bb;//需要指定类域访问B属于AA::B bb;bb.BprintA(aa);return 0; } A类的大小是8字节根据内存对齐规则也就相当于A类中两个int类型所占字节大小B类虽然也有俩int类型但B不是它的成员只是它的友元类受A类的类域限制。下面输出100和10就可以看出内部类就是友元类可以访问另一个类的私有和保护。 //声明友元类 class A {friend class B; //友元声明 B是A的友元类 private:int _a1 100;int _a2 10; };class B { public:void printAB1(const A a){cout a._a1 endl;cout _b1 endl;}void printAB2(const A a){cout a._a2 endl;cout _b2 endl;} private:int _b1 120;int _b2 12; };int main() {A aa;//不需要访问类域找B因为B只是A的友元//并且B不属于A不受A类域的限制B bb;bb.printAB1(aa);bb.printAB2(aa);return 0; } 输出结果 4.匿名对象 上面我们用类型对象名实参定义出来的叫有名对象 匿名对象就是不加对象名直接就是类型实参。 匿名对象生命周期只在当前一行。 class A { public:A(){cout A() endl;}A(int a):_a(a){cout A(int a) endl;}~A(){cout ~A() endl;}void Print(){cout xxxxxxx endl;} private:int _a 100; };int main() {//有名对象不传实参是需要这样定义A a1;//不能这样定义这样定义就类似于一个函数声明如int func() )编译器会分不清//A a1();//有名对象传实参A a2(2);//匿名对象不传实参可以这样定义不用取名字A(); //它声明周期只有这一行下一行它会自动调用析构//匿名对象传实参A(1);//匿名对象的一种用法A().Print(); //这样可以更方便return 0; } 5.类型转换与对象拷贝时的编译器的优化 C支持内置类型隐式类型转换为类类型对象需要有相关内置类型为参数的构造函数。 类类型对象之间也可以隐式类型转换需要相应的构造函数支持。 通俗说C是支持隐式类型转换是要借助构造函数支持的。 若不想让其支持隐式类型转换就在构造函数前面加一个关键字 explicit  即可。 class A { public:A(int a):_a1(a){}A(int a1, int a2):_a1(a1),_a2(a2){}void Print(){cout _a1 _a2 endl;}private:int _a1 1;int _a2 2; };int main() {A a1(100); //调用构造函数传参构造a1.Print();//这里编译器是会优化的//连续的 构造拷贝构造 - 优化为直接构造A aa1 10; //这里是一个隐式类型转换aa1.Print();//C11之后支持多参数转化A aa2 { 11 , 22 };aa2.Print();return 0; } 注意 临时对象都具有常性不能对其修改(权限放大问题。 int i 0; float a i; //这里会发生隐式类型转化//i会产生一个float类型的临时对象变量//但这里a是float引用类型就是这个临时对象的别名//这里会报错就是因为临时对象具有常性//不加const修饰会使权限放大导致报错//需要这样写 const float a i;用explicit修饰 explicit A(int a) //这样构造函数不再支持隐式类型转换:_a1(a) {} 类类型隐式类型转换还需要注意一点 class A { public:A(int a):_a1(a){}A(int a1, int a2):_a1(a1),_a2(a2){}void Print(){cout _a1 _a2 endl;}int GetA() const //这里给B类提供值的成员函数也要用const修饰{return _a1 _a2;}private:int _a1 1;int _a2 2; };class B { public:B(const A a) //若这里拷贝构造用的const修饰:_b(a.GetA()){}void Print(){cout _b endl;} private:int _b; };int main() {A aa2 { 11,22 };aa2.Print();B b1 aa2;b1.Print();return 0; } 可以看出成功的隐式类型转换了。 编译器为了提高程序效率会进行一些优化取决于编译器并且不能影响正确性 //这里vs编译器是会优化的//连续的 构造拷贝构造 - 优化为直接构造A aa1 10; //减少这里临时变量的构造和对aa1的拷贝构造//相当于直接让10构造aa1aa1.Print(); //定义一个A类用来观测VS优化的情况 class A { public:A(){cout A() endl;}A(int a1, int a2):_a1(a1),_a2(a2){cout A(int a1, int a2) endl;}A(const A a):_a1(a._a1),_a2(a._a2){cout A(const int a) endl;}~A(){cout ~A() endl;} private:int _a1 0;int _a2 0; };这是一个正常的带参构造 用一个匿名对象拷贝构造一个A类型对象   可以看出这里被优化成直接构造了 继续用一个匿名对象连续两次的拷贝构造    直接成构造了 这种情况就优化不了了 总结vs下  隐式类型转换连续的构造拷贝构造 - 优化为直接构造一个表达式中连续的构造拷贝构造 - 优化为一个构造连续的拷贝构造拷贝构造 - 优化成构造一个表达式中连续的拷贝构造 赋值重载 - 无法优化 通过对类和对象的深入理解和运用我们能够更加高效地组织代码、提高代码的可维护性和可扩展性。类和对象对我们以后的应用和学习中都发挥着至关重要的作用。 制作不易,若有不足之处或出问题的地方请各位大佬批评指正 感谢大家的阅读支持
http://www.dnsts.com.cn/news/58444.html

相关文章:

  • 线下课程seo嘉兴优化网站费用
  • 连云港建设部网站网站建设与管理中专专业
  • 企业网站的特点是wordpress网站非常慢
  • 怎么建设seo自己网站wordpress 购物插件
  • 建站推广网站排名海南app网站建设
  • 东莞网站建设方案表互联网网站案例
  • 上海 互联网公司优化一个网站需要多少钱
  • 互动网站建设特点如何跟帖做网站
  • 政务网站建设工作总结网页制作手机版下载
  • 网站建设费属哪个费用金螳螂家装官网
  • 天津市做网站的公司台州seo
  • 手机网站可以做百度商桥吗手机作图软件app
  • 怎样给公司做一个网站微信公众号关注
  • 苏省住房和城乡建设厅网站eclipse的开发网站开发
  • 中劳网做网站承德做网站优化
  • 网站基础建设和管理网站优化seo教程
  • 网站开发培训内容南京城乡建设局网站
  • 做网站js是什么手机网站图片自适应
  • 国产99做视频网站哈尔滨seo优化大家
  • 成都私人做网站三门峡设计公司官网
  • 网站开发运行环境成都定制软件开发公司
  • dedecms手机网站外贸英文网站制作
  • 开不锈钢公司怎么做网站想开发一个网站需要怎样做
  • 做vr网站百度客户电话
  • 做网站系统的答辩ppt范文手机配件网站模板
  • 知名做网站广州免费建站找哪家
  • 东莞企业网站制作金泉网做网站找谁
  • 单位做网站有哪些网站建设火凤凰
  • 网站建设学习什么织梦旅游网站
  • 免费下载网站设计方案wordpress刷新才显示