一:类的定义。
类与C语言中的结构体十分相似,类是以关键字class开头,在加上class name 为类名,{ }中为类的主体,注意在类定义结束的时候要在括号后加入分号{};
类中的元素成为类的成员:类中的数据恒威类的属性或者成员变量;类中的函数成为类的方法后者成员函数
类的定义有两种方法:
- 声明与定义都放入类中。(注意:成员函数如果在类中定义,编译器可能会将其当做内联函数来处理)
- 声明放入.h文件中,类的定义放在.cpp文件中
二:类的访问限定符及封装
访问限定符:
实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择的将其接口提供给外部用户使用。
1.public修饰的成员在类外可以直接被访问
2.protected和private修饰的成员在类外不能直接访问(此处protected和private是类似的)
3.访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4.class的默认访问权限为private, struct 为public(因为struct 要兼容C语言)
注意:访问限定符只有在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别。因为兼容了C语言的原因,struct 在C++中可以当作结构体来使用,也可以用来定义类,与class 定义类是一样的,区别是struct 的成员默认访问方式时public ,class 的成员默认访问方式是private.
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
类的作用域:
类的所有成员都在类的作用域中,在类体外面定义的成员,需要使用:: 作用域解析符指明成员属于哪个类域。
1 class Person { 2 public: 3 void PrintPersonInfo(); 4 private: 5 char _name[20]; 6 char _gender[3]; 7 int _age; 8 }; 9 10 // 这里需要指定PrintPersonInfo是属于Person这个类域 11 void Person::PrintPersonInfo() { 12 cout<<_name<<" "_gender<<" "<<_age<
三:类的实质化
实质:用类类型创建对象的过程,称为类的实质化
1.类只是一个模型,定义出一个类并没有分配实际内存空间来储存它
2.一个类可以实际化出多个对象,实际化出的对象占用实际的物理空间,存储类成员变量
3.类实例化出对象是没有实体的。
四:类对象模型
计算类对象的大小:
一个类的大小,实际就是该类中”成员变量“之和,当然也要进行内存对齐,注意空类的大小,比价特殊,编译器给了空类一个字节来唯一标识这个类。
类的大小与结构体相似:
1.第一个成员在与结构体偏移量为0的地址处
2.其他成员变量要对齐到每个数字(对齐数)的整数倍的地址处。
(注意:对齐数 = 编译器默认的一个对齐数于该成员大小的比较值)
(vs 中默认的对齐数为8, gcc 中的对齐数位4)
3.结构体总大小为:最大对齐数(所有变量类型最大者于默认对齐参数取最小)的整数倍
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最打对齐数的整数倍处,结构体的整体大小就是所有最大对齐数
(含嵌套结构体的对齐数)的整数倍
五:this指针
编译器给每个“成员函数”增加 了一个隐藏的指针参数。让该指针指向当前对象(函数与运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
特性:
- this指针的类型:类类型*const
- 只能在“成员函数”的内部使用
- this 指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this 形参。所以对象中不存储this 指针。
- this 指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
六:类的6个默认成员函数
1.构造函数
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只调用一次。
特性:
1.函数名与类名相同
2.无返回值
3.对象实例化时编译器自动调用对应的构造函数。
4.构造函数可以重载
1 class Date { 2 public : 3 // 1.无参构造函数 4 Date () {} 5 6 // 2.带参构造函数 7 Date (int year, int month , int day ) { 8 _year = year ; 9 _month = month ; 10 _day = day ; 11 } 12 private : 13 int _year ; 14 int _month ; 15 int _day ; 16 };
如果在类中没有自己去写一个构造函数,那么编译器或默认的自动生成一个无参的默认构造函数,一但用户自己去实现了构造函数,那么编译器就不去实现。
注意:无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
2.析构函数
析构函数是特殊的成员函数。
特征:
1.函数名是在类名前加~字符
2.无参数无返回值
3.一个类有且只有一个析构函数。若未显示定义,系统会自动生成默认的析构函数
4.对象生命周期结束时,C++编译系统自动调用析构函数
1 typedef int DataType; 2 class SeqList { 3 public : 4 SeqList (int capacity = 10){ 5 _pData = (DataType*)malloc(capacity * sizeof(DataType)); 6 assert(_pData); 7 _size = 0; 8 _capacity = capacity; 9 }10 ~SeqList(){ 11 if (_pData){ 12 free(_pData ); // 释放堆上的空间 13 _pData = NULL; // 将指针置为空 14 _capacity = 0; 15 _size = 0; 16 } 17 } 18 private : 19 int* _pData ; 20 size_t _size; 21 size_t _capacity; 22 };
3.拷贝构造函数
只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
特征:
- 拷贝构造函数时构造函数的一个重载形式
- 拷贝构造函数的参数只有一个且必须使用引用传参,使用传参方式会引发无穷递归调用。
-
构造 函数体中的语句只能将其称作为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内 可以多次赋值。
1 class Date { 2 public: 3 Date(int year = 1900, int month = 1, int day = 1) { 4 _year = year; 5 _month = month; 6 _day = day; 7 } 8 9 Date(const Date& d) { 10 _year = d._year; 11 _month = d._month; 12 _day = d._day; 13 } 14 private: 15 int _year; 16 int _month; 17 int _day; 18 };19 20 int main() { 21 Date d1; 22 Date d2(d1);23 24 return 0; 25 }26
初始化列表:
以冒号开始,接着是一个以逗号分隔的数据成员列表,每个“成员变量”后面跟一个放在括号中的初始值或表达式。
1 //例如2 Date(int year, int month, int day) 3 : _year(year) 4 , _month(month) 5 , _day(day) 6 {}
注意事项:
1.每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2.类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量
const成员变量
类类型成员(该类没有默认构造函数)
1 class A { 2 public: 3 A(int a) 4 :_a(a) 5 {} 6 private: 7 int _a; 8 }; 9 10 class B { 11 public: 12 B(int a, int ref) 13 :_aobj(a) 14 ,_ref(ref) 15 ,_n(10) 16 {} 17 private: 18 A _aobj; // 没有默认构造函数 19 int& _ref; // 引用 20 const int _n; // const 21 };
在这里建议使用初始化列表进行初始化,成员变量在类类型中的声明次序就是其在初始化列表中的初始化顺序,与其在初始化列中的先后次序无关。
4.赋值运算符重载
运算符重载:具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数原型:返回值类型 operator操作符(参数列表)
注意:
- 不能通过连接其他符号来创建新的操作符:比如operator
- 重载操作符必须有一个类类型或者枚举类型的操作数
- 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不能改变其含义
- 作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参
- .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载
1 //这里简单的写一个日期类的运算符重载2 bool operator==(const Date& d1, const Date& d2) 3 { 4 return d1._year == d2._year 5 && d1._month == d2._month 6 && d1._day == d2._day; 7 }8
赋值运算符重载
赋值运算符主要有四点:
1.参数类型
2.返回值
3.检测是否自己给自己赋值
4.返回*this
5.一个类如果没有显示定义赋值运算符重载,编译器也自动生成一个,完成对象按字节序的值拷贝
1 Date& operator=(const Date& d) 2 { 3 if(this != &d) 4 { 5 _year = d._year; 6 _month = d._month; 7 _day = d._day; 8 } 9 }
七.const成员
1.const修饰类的成员函数
将const修饰的类成员函数称之为cosnt成员函数,const修饰类成员安徽念书,实际此修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
1 void Display () const { 2 cout<<"Display () const" <
例如上面的代码中const 修饰的是函数中隐含的this指针,表明函数类的变量是不能修改的。
八:友元
友元的分为友元函数和友元类
1.友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声 明,声明时需要加friend关键字
说明:
- 友元函数可访问类的私有成员,但不是类的成员函数
- 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
- 一个函数可以是多个类的友元函数 友元函数的调用与普通函数的调用和原理相同
2.友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
说明:
- 友元关系是单向的,不具有交换性
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time 类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
- 友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元。
九:内部类
概念:
如果一个类定义在另一个类的内部,这个内部类就叫做内部类。注意此时这个内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去调用内部类。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类。注意友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
特性:
1. 内部类可以定义在外部类的public、protected、private都是可以的。
2. 注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。
3. sizeof(外部类)=外部类,和内部类没有任何关系。
1 class A { 2 private: 3 static int k; 4 int h; 5 public: 6 class B { 7 public: 8 void foo(const A& a) { 9 cout << k << endl;//OK 10 cout << a.h << endl;//OK 11 } 12 }; 13 };14 15 int A::k = 1;16 17 int main() { 18 A::B b; 19 b.foo(A()); 20 return 0; 21 }