C++类与对象学习笔记
0.类和对象概述
C语言
是面向过程
的,关注的是过程,分析出求解问题的步骤
,通过函数调用逐步解决问题
。C++
是基于面向对象
的,关注的是对象
,将一件事情拆分成不同的对
象,靠对象之间的交互完成
。
1.类和对象的概念
类
是对一组性质相同的事物
的描述,包括事物的属性和方法
,对象
是类的一个实例
。
例如,老师是一个类,C++老师就是老师类的一个对象
2.封装的理解
面向对象的三大特性:封装
、继承
、多态
- 1.封装是
面向对象程序设计
最基本的特性。把数据(属性)
和函数(方法)
合成一个整体,这在计算机世界中是用类和对象实现的
。(把属性和方法进行封装)
- 2.把
客观事物封装成抽象的类
,并且类可以把自己的属性和方法只让可信的类或者对象操作
,对不可信的类或对象
进行信息的隐藏。(对属性和方法进行访问控制)
3.根据C语言的结构体引入类
C语言
中,结构体中只能定义变量
,在C++
中,结构体内不仅可以定义变量,也可以定义函数
。
struct stu
{
//定义变量(数据)
int num;
int age;
//定义函数(方法)
void Learn()
{
cout << "学生正在睡觉" << endl;
}
};
c++
支持用struct
定义类,是为了学习者可以方便从C语言过渡
。在C++中,定义一个类
,更多的使用class
。
class stu
{
//定义变量(数据)
int num;
int age;
//定义函数(方法)
void Learn()
{
cout << "学生正在睡觉" << endl;
}
};
4.类的定义
class ClassName{
//成员变量
//成员函数
};
class
为定义类的关键字
,ClassName
为类的名字
,{}中为类的主体
,注意类定义结束时后面分号
。类中的元素称为类的成员
,类中的数据称为类的属性或者成员变量
; 类中的函数称为类的方法或者成员函数
。
类的两种定义方式:
声明和定义
全部放在类体中
class Tearch
{
public:
char* _name;
char* _num;
int age;
public:
void Sleep()
{
cout << "老师正在睡觉" << endl;
}
};
注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理
声明
放在.h
文件中,类的定义
放在.cpp
文件中
//.h中
class stu
{
char* _name;
int _num;
int _age;
//方法的声明
void Learn();
};
//.cpp中
void stu::Learn()
{
cout << "学生正在上课" << endl;
}
注:::
是类域限定符,也是命令空间域的限定符,表明你正在定义那个类中的函数。一般情况下,更期望采用第二种方式。
5.类的访问限定符及封装
C++实现封装
的方式是用类将对象的属性与方法结合在一块
,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用
。
访问限定符分为:
public
公有private
私有protected
受保护的
分别进行说明:
public修饰
的成员在类外可以直接被访问
protected
和private
修饰的成员在类外不能直接被访问
- 访问
权限作用域
从该访问限定符出现的位置开始
直到下一个访问限定符出现时为止
class
的默认访问权限
为private
,struct
为public
(因为struct要兼容C
)
注意:访问限定符只在编译
时有用,当数据映射到内存后,没有任何访问限定符上的区别
面试题:
- 如何在类外访问一个类中私有的成员变量?
我们可以根据类内的公有方法去访问类内私有的成员变量
C
语言和C++
中struct
的区别?C++
中struct
和class
区别?
C中struct里只能存在变量,不能存在方法,而C++中的struct既可以存在变量,也可以存在函数方法。C++中的struct定义的类默认访问权限是public,而class定义的类是private
6.类的作用域
类
定义了一个新的作用域
,类的所有成员都在类的作用域
中。在类体
外定义成员,需要使用:: 作用域解析符指明成员属于哪个类域
。
作用域可以分为以下几类:
- 局部域
- 全局域
- 类域
- 命名空间域
namespace N1
{
int a = 10;
void TestFunc()
{
cout << "TestFunc" << endl;
}
}
int a = 20;
class A1
{
public:
void setA(int a){
this->a = a;
}
void PrintA(){
cout << a << endl;
}
private:
int a;
};
int main()
{
A1 b;
b.setA(30);//把类中私有的a设置为30
b.PrintA();//打印的是30
cout << N1::a << endl;//打印的是命名空间中的a=10
cout << a << endl;//打印的是全局域的a=20
return 0;
}
注意: 尽量避免成员函数的参数
与成员变量同名
。成员变量
在类中具有全局作用域属性
7.类的实例化
用类类型创建对象
的过程,称为类的实例化
。例如:给你一张建筑图纸(代表类)
,然后根据图纸修建房子,这个房子就代表对象
。
注意点:
- 类只是一个
模型一样
的东西,限定了类有哪些成员
,定义出一个类并没有分配实际的内存空间
来存储它 - 一个类可以
实例化出多个对象
,实例化出的对象占用实际的物理空间,存储类成员变量
class Tearch
{
public:
char* _name;
char* _num;
int age;
public:
void setVal(char* name,char* num,int _age) {
_name = name;
_num = num;
_age = age;
}
void sleep()
{
cout << _name << "老师正在睡觉" << endl;
}
};
int main()
{
Tearch t;//根据类Tearch实例化对象
t.setVal("zhangsan", "123", 10);
t.age = 10;
t.sleep();
return 0;
}
8.计算类对象的大小
创建一个对象时的内存布局:只保存成员变量,而成员函数存放在公共的代码段
所以我们在计算一个对象的大小
时,只需要计算包括其成员变量的大小
,不用考虑成员函数
。只计算成员变量的大小和计算结构体大小是一样的
,根据内存对齐规则
直接去计算即可。
// 类中既有成员变量,又有成员函数,4个字节
class A1
{
public:
void f1()
{}
void f2()
{}
private:
int _a;
};
// 类中仅有成员函数,大小为1个字节,代表进行占位,不存储数据,表示对象存在过
class A2
{
public:
void f1()
{}
void f2()
{}
};
// 类中什么都没有---空类,也是1个字节,为了标识这个类
class A3
{};
//总大小为4的整数倍数既16
class A4
{
char c1;//0
int a;//4-7
char c2;//1
}
结构体内存对齐规则:
- 第一个
成员在与结构体偏移量为0
的地址处 - 其他成员变量要对齐到某个数字
(对齐数)的整数倍的地址处
。注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值,VS中默认的对齐数为8,gcc中的对齐数为4 - 结构体总大小为:
最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
- 如果嵌套了结构体的情况,
嵌套的结构体对齐到自己的最大对齐数的整数倍处
,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
为什么存在内存对齐:
-
空间换取时间
,内存是按整数倍(vs是8个字节)的位置访问的
-
平台移植原因:
不是所有的硬件平台
都能访问任意地址的任意数据
,某些硬件平台只能在某些地址处取某些特定类型的数据
,内存对齐可以增强程序的可移植性
9.this指针
this指针指向当前对象
,谁调用它谁就是谁
。存储在栈
上,实质是一个形参指针
。(widows
下vs也可用ecx寄存器传递
)
class Date
{
private:
int _year;
int _month;
int _day;
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
void SetDate(int year, int month, int day){
this->_year = year;
this->_month = month;
this->_day = day;
}
};
int main()
{
Date d1;
Date d2;
d1.SetDate(2018, 9, 2);
d2.SetDate(2018, 9, 3);
d1.Print();
d2.Print();
return 0;
}
注:成员变量可以显示的加上this,但是成员函数是不能加 this。
class Date
{
private:
int _year;
int _month;
int _day;
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
void SetDate(int year, int month, int day){
this->_year = year;
this->_month = month;
this->_day = day;
}
void sleep(){
cout<<"正在睡觉"<<endl;
}
};
int main()
{
//访问printf用到了this,对空指针操作
Date* pd = NULL;
pd->Print();
//sleep函数没用对this指针操作,可以编译通过
pd->sleep();
}
注:this指针可以为空
,调用不使用 this的方法可以,但是调用使用this的方法不行
C++编译器给每个“成员函数“
增加了一个隐藏的指针参数
,让该指
针指向当前对象(函数运行时调用该函数的对象)
,在函数体中所有成员变量的操作,都是通过该指针去访问
。只不过所有的操作对用户是透明的,即用户不需要来传递
,编译器自动完成
。
this指针的特性:
- this指针的类型:
类类型* const
- 只能在
“成员函数”
的内部使用 - 时时刻刻指向当前对象,
不属于对象
的一部分,不会影响sizeof的结果
this指针
是成员函数第一个隐含的指针形参(存放在栈帧上)
,一般情况由编译器通过ecx寄存器自动传递
,不需要用户传递