C++入门篇(4)—— 类与对象(1)
目录
1.类的引入
2.类的定义
3.类的访问限定符
4.类的作用域
5. 类对象的存储方式
6. this指针
6.1 this指针的引入
6.2 this指针的特性
6.3有意思的面试题
1.类的引入
C语言struct 结构体中只能定义变量,而C++中可以定义函数。
struct Date
{
void Init(int year = 2023, int month = 12, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
int _year;
int _month;
int _day;
};
就像这样一个日期结构体,可以在内部定义函数,这就是一个类。在C++中,更喜欢用class来定义类。
2.类的定义
class className //指定想要的类的名字
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
这 就是类的定义方式。类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。
类中的函数有两种定义方式
1.直接在类中定义函数,但需要注意,这样编译器可能将该函数视为内联函数。
2.类中函数声明,在类外定义函数。在类外定义函数需要注意成员函数名前加上类名。
struct Date
{
void Init(int year = 2023, int month = 12, int day = 1);
int _year;
int _month;
int _day;
};
//注意成员函数名前加上类名
void Date::Init(int year = 2023, int month = 12, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
3.类的访问限定符
访问限定符会对访问类进行的操作进行限制,有三个访问限定符:public(公有)、private(私有)、protected(保护)
具体用法如下
class Date
{
public:
void Init(int year = 2023, int month = 12, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
这样外部只能访问Init这个成员函数而不能访问到类中的三个成员变量。 目前可以认为private和protected区别不大。
需要注意的是,class默认访问权限是private,而struct默认访问权限是public(为了兼容C),
访问限定符的作用范围是到下一个访问限定符或 } 为止。
访问限定符的出现是为了更好的管理,C语言中的结构体访问过于宽松自由以至于可能会造成一些意想不到的bug。而C++中的类可以限制对类对象的访问,这样更加安全。
4.类的作用域
类会有一个新的作用域,在外部定义类成员时,需要指定类名。
5. 类对象的存储方式
类对象中成员变量是存储在类对象中的,而会多次被调用的成员函数,并不会存储在类对象中,否则定义多个类对象会造成较大的空间浪费,因为每个类对象中都存储有同一个函数地址,这显然是不合理的,会造成很大的浪费。
class Date
{
public:
void Init(int year = 2023, int month = 12, int day = 1);
private:
int _year;
int _month;
int _day;
};
void Date::Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
int main()
{
Date d;
d.Init();
cout << sizeof(d) << endl;
return 0;
}
运行这段代码会发现结果是12,这说明成员函数并不存在类对象中。
成员函数被存放在公共的代码段。
这里注意一下,类的大小计算方式和结构体对齐规则相同,并且空类大小为1,不是0。
6. this指针
6.1 this指针的引入
class Date
{
public:
void Init(int year = 2023, int month = 12, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
先看这个类,我们在调用成员函数时是这样调用的
int main()
{
Date d1;
d1.Init();
Date d2;
d2.Init();
return 0;
}
我们传参时并没有传入给哪个对象调用这个函数的信息。举个例子来理解一下。
C语言中要实现Date 类中的这个Init函数功能,一定会是这样的一个函数
void Init(struct Date* pd, int year, int month, int day);
总之一定会把对哪个对象调用函数的有关信息给写入形参列表,这个信息可能是那个对象的地址,
这样才能确定对哪个对象调用函数。
而C++中的成员函数都存放在公共的代码段,其中并没有任何关于对象的信息,如何确定对哪个对象操作呢?
实际上,C++在这里跟C语言的处理手法是一样的,同样传入了一个地址,用一个指针变量来接收,这样函数就能知道对哪个对象进行操作了。
6.2 this指针的特性
this指针类型:类类型* const ,对Date类来说,this指针类型就是Date* const,这意味着,this指针的值,是不能修改的。
this指针只能在成员函数内部使用。
this指针本质是成员函数的形参,是成员函数的一部分,是不会存在对象内部的。
this是由编译器自动传递的,不需要用户自己手动传递。
6.3有意思的面试题
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
相信一般都会认为会运行崩溃吧,但是实际结果既出人意料又在情理之中。结果是A。
根据上面对this指针的讲解,我们可以理解其实这里的p只是会传给Print作为形参,而Print函数中没有对p作解引用操作,因此自然不会崩溃,而是正常运行。