赞
踩
目录
- class className{
-
- }; //注意分号
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面的分号。类中的元素为类的成员;类中的数据成为类的属性或者成员变量;类中的函数成为类的方法或者成员函数。
类的两种定义方式:
1、声明和定义都放在类体中(注意:成员函数如果在类中定义,编译器可能会将其当作内联函数处理)
- class Person {
- public:
- void show() {
- cout << _name << "-" << _sex << "-" << _age << endl;
- }
- public:
- char* _name;
- char* _sex;
- int _age;
- };
2、声明放在头文件当中,定义放在源文件中。
一般情况下,推荐方式二。
种类:public(公有);protected(保护) ;private(私有的)
1、public修饰的成员在类外可以直接被访问
2、protected和private修饰的成员在类外不能直接被访问
3、访问权限的额作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止
4、class的默认访问权限为private,struct为public(因为struct要兼容C)
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别。
C++中的作用域:全局作用域、函数体内部的局部作用域、命名空间、类域
类域:类定义了一个新的作用域,类的所有成员都在类的作用域中。在类外定义成员,需要使用::作用域解析符指明成员属于哪个类域。
用类类型的创建对象的过程成为类的实例化
类只是一个模型一样的东西,限定了类有那些成员,定义出一个类并没有分配实际的内存空间来存储它。
一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。
计算类对象的大小
一个类的大小,实际就是该类中成员变量之和,当然也要进行内存对齐。
空类的大小:
对于空类,编译器为了区分同一个类创建出来的不同的对象时,在主流编译器中,为其分配一个字节作为占位符。
C++编译器给每个“非静态的成员函数”增加一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
this指针的特性:
1、this指针的类型:类类型* const
2、只能在"成员函数"的内部使用
3、this指针本质上是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
4、this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
this指针存在哪里?
this指针存放在栈上。
验证:定义this的引用类型变量,打印this的地址与esp(栈顶)ebp(栈底)地址比较,如果在两者之间,就说明是在栈上定义的。
this指针可以为空吗?
this指针可以为空,当this指针为空的时候,如果在成员函数中没有访问任何成员变量或者成员函数,则代码不会崩溃,否则就会崩溃。
构造函数是一个特殊的成员函数,名字与类名相同,创建类对象时由编译器自动编译,保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只调用一次。
特点:
1、函数名与类名相同
2、没有返回值类型
3、创建类类型的对象的时候由编译器自动调用
4、在对象整个生命周期中只调用一次
5.、构造函数可以重载,也可以在定义时给一个全缺省参数(无参和全参一次搞定)
但此时不能再用默认无参构造函数了,如果想用无参构造,可以传递全缺省参数。
编译器生成的默认的构造函数到底有什么用?
C++把类型分为内置类型(基本类型)和自定义类型。内置类型就是语法已经定义好的类型:如int/char,自定义类型就是我们使用class/struct/union自己定义的类型,看下面的程序,就会发现编译器生成默认的构造函数会对自定义类型成员_t调用它的默认函数。
- class Time
- {
- public:
- Time()
- {
- cout << "Time()" << endl;
- _hour = 0;
- _minute = 0;
- _second = 0;
- }
- private:
- int _hour;
- int _minute;
- int _second;
- };
-
- class Date {
- private:
- int _year;
- int _month;
- int _day;
- Time _t;
- };
-
- int main() {
- Date d2;
- return 0;
- }

概念:
是一个特殊的成员函数,析构函数不是完整对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
特点:
1、函数名是:~类名
2、无参数,无返回值
3、一个类有且仅有一个析构函数。若未显示定义,系统会自动生成默认的析构函数
4、对象生命周期结束时,C++编译系统自动调用析构函数
5、编译器自动生成的析构函数,会对自定义类型成员调用它的析构函数。
注意:只要涉及到资源的申请,那么析构函数必须要程序员手动实现,默认的析构函数是无法达到我们的要求的。
概念:只有单个形参,该形参是对本类类型对象的引用(一般使用const修饰),在用已存在的类类型对象创建新对象的时候,由编译器自动调用。
特性:
1、拷贝构造函数是构造函数的重载形式
2、拷贝构造函数的参数只有一个,并且必须使用引用传参,使用传值方式会无穷无尽的递归
3、若未 显示定义,系统生成默认的 拷贝构造函数。默认的拷贝构造函数对象按内存存储,按字节序完成拷贝,这种拷贝我们叫做浅拷贝或者值拷贝。
浅拷贝在不涉及资源申请的类中可以正常使用,但是在涉及资源申请的类中是无法正常使用的。后者会在释放申请资源的时候,调用两次析构函数,导致程序崩溃。
4、默认生成的拷贝构造不能满足申请资源的情况,因此需要我们实现深拷贝。
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
作为类成员的重载函数:
注意事项:
1、不能通过连接其他符号来创建新的操作符:比如operator@
2、重载操作符必须有一个类类型或者枚举类型的操作数
3、用于内置类型的操作符,其含义不能改变。例如内置类型的+,不能改变其含义
4、作为类成员的重载函数时,其形参看起来比操作数数目少一,成员函数的操作符有一个默认的形参this限定为第一个参数。
5、五个运算符不能重载:.*、::、sizeof、三目运算符?:、点.。
1、形式
返回值类型 operator= (参数){......}
2、参数类型:
一般都是类类型对象的引用,相较于传值这样就会少了形参实例化的拷贝构造
3、返回值:
一般返回*this
是类类型对象的引用
4、检测是否在给自己赋值
if(this != &d)
一个类如果没有显式定义赋值运算符重载函数,编译器也会生成一个,完成对象按字节序的值拷贝(浅拷贝)。
和前面一样,对于需要申请资源的类来说,赋值运算符重载依旧需要我们自己编写,否则程序在最后析构的时候就会发生崩溃。-
将const修饰的类成员函数称之为const成员数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
1、const对象可以调用非const成员函数吗?
答案是不行的,在传参的时候传的实参是const Date* this,而形参却是Date* this ,在调用的时候属于权限的放大。
2、非const对象可以调用const成员函数吗?
可以,此时实参是Date* this,而形参是const Date* this,在调用的时候属于权限的缩小。
3、const成员函数内可以调用其他的非const成员函数吗?
在调用的时候,形参为const Date* this,实参为Date* this,属于权限的放大。
4、非成员函数内可以调用其他的const成员函数吗?
此时实参为Date* this,形参为const Date* this,属于权限的缩小。
这两个默认成员函数一般不用重新定义,编译器默认会生成。
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况才需要重载,比如想获取到指定的内容。
构造函数体中的语句只能将其称之为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
如下:编译代码后,报错说引用类型未初始化,这就说明构造函数中做的工作是赋值,而不是初始化。
注意:
1、每个成员变量在初始化列表中只能出现一次(初始化只能只能初始化一次)
2、类中包含以下成员,必须放在初始化列表位置进行初始化:引用成员变量、const成员变量、自定义类型成员(该类没有默认构造函数)
对于没有默认构造函数的自定义类型成员,如果没有自定义初始化列表,编译器就会自动生成,对于基本类型会初始化为随机值;而对于自定义类型,编译器会调用默认构造函数,因为没有默认构造函数,所以会报错。
3、尽量使用 初始化列表初始化,因为不管是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化 。
4、成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
构造函数不仅可以构造与初始化对象,对于单个参数的构造函数,还具有类型转换的作用。如下:
加上关键字,将会禁止单参构造函数的隐式转化:
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用 static修饰的成员函数称之为静态成员函数。静态的成员变量一定要在类外进行初始化。
特性:
1、静态成员变量为所有类对象所共享,不属于某个具体的实例,不影响sizeof(对象)的大小。
2、静态成员变量必须在类外定义,定义时不添加static关键字
3、静态成员变量即可用类名::静态成员或者对象.静态成员来访问
4、静态成员变量不能放在构造函数初始化列表的位置进行初始化
5、静态成员变量访问收到访问限定符的限定
面试题:实现一个类,计算程序中创建了多少类对象。
特性:
1、静态成员函数中没有隐藏的this指针
2、静态成员函数中不能访问普通成员变量,不能调用普通成员函数
3、静态成员函数可以调用静态成员函数或者访问静态成员变量
4、静态成员函数不能设置为const成员函数------->静态成员函数没有this指针
友元分为:友元函数和友元类
友元提供了一种突破封装的方式,有时提供了遍历。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
当我们尝试重载operator<<的时候,会发现我们没办法将operator<<重载成成员函数:
这样就可以了:
这个问题其实是因为cout的输出对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以我们要将operator<<重载成全局函数。但是这样的话,又将导致类外没办法访问成员,这里就需要用友元来解决。
友元函数的使用:
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字:
流插入运算符<<重载时的约定:第一个参数必须是ostream&第二个参数才是要打印的内容。
返回值类型必须是输出流对象的引用,支持连续输出。
友元函数注意事项:
友元函数可访问类的私有和保护成员,但不是类的成员函数。
友元函数不能用const修饰
友元函数可以在类定义的任何地方声明,不受访问限定符的限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用和原理相同。
友元类的所有成员函数都可以时另一个类的友元函数,都可以访问另一个类的非共有成员。
友元函数是单向的,不具有交换性。
友元关系不能传递。如果B是A的友元,C是B的友元,则不能说明C是A的友元。
概念:
如果一个类定义在另一个类的内部,这个就叫做内部类。注意此时这个内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去调用内部类。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类。注意友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
特性:
内部类可以定义在外部类的public、proctected、private都是可以的。
注意内部类可以直接访问外部类的static、枚举成员、不需要外部类的对象/类名
sizeof(外部类) = 外部类 ,和内部类没有任何关系
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。