在 C++中,引入了一个新的定义----------类。类是一种用户自定义的数据类型,用于封装数据和行为。类可以看作是一个模板或蓝图,描述了一组相关的数据和对这些数据的操作方式。通过类,可以创建具体的对象来使用和操作这些数据。
类是C++面向对象编程的核心,为了便于理解,我们可以将类抽象成一架飞机:
学习了C语言之后,我们知道了结构体类型,但在C语言结构体内只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
我们在C++中,用struct实现链表,可以将我们的一系列函数放进去,而这些在C++中更喜欢用class来代替。
在 C++ 中,struct
和 class
都可以用于定义自定义数据类型,它们的区别主要有以下两点:
默认访问权限不同:struct
的默认访问权限是公有的,而 class
的默认访问权限是私有的。这意味着在 struct
中定义的成员变量和成员函数是公有的,可以被外部访问;而在 class
中定义的成员变量和成员函数是私有的,只能在类内部访问。
继承方式不同:在没有显式指定继承方式时,class
的继承方式是私有继承(private
),而 struct
的继承方式是公有继承(public
)。即如果一个类没有指定继承方式,则其默认继承方式与其类型相同。
类的基本定义形式是:
class className
{
// 类体:由成员函数和成员变量组成
};
类体内的内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为成员函数。
这里注意!在类内直接定义的函数 会被默认视为内联函数,但并不意味着所有在类内定义的函数都是内联函数。
内联函数是一种编译器优化的机制,它将函数的定义体直接插入到调用该函数的地方,而不是通过函数调用的方式执行。这样可以减少函数调用的开销,提高程序的执行效率。
就像这样直接插入:
在类内定义的函数,默认情况下会被视为内联函数。但是,如果函数体过于庞大复杂,编译器可能会忽略内联函数的请求,将其视为普通函数进行处理。因此,并不是所有在类内定义的函数都会被真正地内联。。。
通过使用 inline
关键字,可以告诉编译器将该函数作为内联函数处理。
? 需要注意的是,内联函数适用于简单短小的函数,且在多处调用的情况下才能发挥优势。对于复杂的函数或只在少数地方调用的函数,使用内联可能导致代码膨胀,反而降低性能。因此,在确定是否使用内联函数时,需要综合考虑函数的规模、调用频率以及性能需求等因素。
?当我们单纯的定义一个类,定义任何成员变量时,系统会为这个类开空间吗
在 C++ 中,一个类的对象大小至少为 1 字节,即使这个类没有任何成员变量。这是因为每个对象都必须在内存中占据至少一个字节的空间。
这个字节通常用于对象的地址,也就是对象所在内存地址的第一个字节。即使这个字节不存储有效数据,但是它也是必需的,因为它用于确定对象在内存中的位置和边界。
在C++中,类的访问限定符用于控制类的成员变量和成员函数的访问权限。C++中为我们提供了三种访问限定符:public、private和protected。
1. public:
2. private:
3. protected:
默认情况下,C++中的类成员的访问限定符是private。这意味着如果没有显式地指定访问限定符,那么成员变量和成员函数都将被视为private成员。
代码演示:
#include <iostream>
using namespace std;
class MyClass {
public:
int publicVar;
void publicFunc() {
cout << "publicFunc is called" << endl;
}
protected:
int protectedVar;
void protectedFunc() {
cout << "protectedFunc is called" << endl;
}
private:
int privateVar;
void privateFunc() {
cout << "privateFunc is called" << endl;
}
};
int main() {
MyClass obj;
obj.publicVar = 1;
obj.publicFunc();
return 0;
}
在上面的代码中,我们定义了一个名为MyClass的类,并且使用了三种不同的访问限定符来限制成员的访问权限。在main函数中,我们创建了一个MyClass类的对象obj,并且访问了publicVar和publicFunc方法,因为它们都是公共成员。但是我们不能访问protectedVar、protectedFunc、privateVar和privateFunc方法,因为它们只能在类内部访问。如果尝试访问它们,编译器会报错。
通过使用不同的访问限定符,我们可以控制类的成员在何处可见和可访问。这有助于实现信息隐藏和封装的概念,使得类的实现细节对于外部用户来说是不可见的,从而提高代码的安全性和可维护性。
在C++中,类的作用域是指类中成员的可见范围。类的作用域可以分为两个部分:类的内部作用域和类的外部作用域。
::
来限定。在成员函数中,可以直接访问该类的所有成员,包括私有、保护和公共成员。::
来限定访问类中的成员。通过类名加上作用域解析运算符,可以访问该类的公共成员和静态成员。私有和保护成员无法在类外部直接访问。在类体外定义成员时,需要使用 ::
作用域操作符指明成员属于哪个类域。作用域操作符在博主C++入门篇 有讲解
类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图。
类在内存中的存储模式如下:
在计算类对象的大小时,一般只需要考虑成员变量的大小,而不需要计算成员函数的大小。成员函数在对象中并不占据实际的内存空间,它们只是一组指向类的成员函数代码的指针。这些指针共享存储在类的静态区域中,并且对于每个类的所有对象来说是相同的。因此,成员函数不会影响类对象的大小。
总结起来,计算类对象的大小只需要考虑成员变量的大小,而成员函数和虚函数表对于类对象的大小没有直接影响。
在C++中,每个对象都有一个指向自己的指针,这个指针就是this指针。this指针是一个隐含于每个非静态成员函数中的特殊指针,它指向调用该函数的对象。在C++中,成员函数可以直接访问对象的数据成员,因为在成员函数内部,this指针指向当前对象的地址,通过this指针可以访问对象的所有成员变量和成员函数。
先来看一段简单的代码:
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
int main()
{
Date d1, d2;
d1.Init(2022,1,11);
d2.Init(2022, 1, 12);
d1.Print();
d2.Print();
return 0;
}
???Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
在C++中,对象在调用成员函数时,编译器会自动将该对象的地址作为this指针传递给成员函数。在Init函数中,通过this指针访问对象的成员变量,确保将值设置到正确的对象中。
因此,当d1调用Init函数时,Init函数内部的操作会通过this指针访问d1对象的成员变量,即将值设置到d1对象的_year、_month和_day成员变量中;同样,当d2调用Init函数时,Init函数内部的操作会通过this指针访问d2对象的成员变量,将值设置到d2对象的成员变量中。这样就实现了不同对象之间的区分。当然最后,当d1和d2调用Print函数时,会打印出各自的年月日信息。
只不过这些所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
this指针在C++中具有以下特性:
this指针的类型是类类型的指针,即类类型* const。这意味着this指针是一个常量指针,指向当前对象的地址,不可以被修改。
this指针只能在成员函数内部使用。它代表当前对象的地址,并且只有在成员函数内部才能访问到该指针。
this指针本质上是成员函数的一个隐式形参。当对象调用成员函数时,编译器会将对象的地址作为实参传递给this指针,使得成员函数可以通过this指针来访问对象的成员变量和成员函数。
this指针是成员函数的第一个隐含参数,一般情况下由编译器自动传递,不需要用户显式传递。
对象本身并不存储this指针,它只是在成员函数调用时被传递给this指针。
通过使用this指针,我们可以在成员函数内部访问对象的成员变量和函数,确保每个对象在调用成员函数时可以正确地操作自己的数据。this指针的存在使得对象的成员函数可以区分不同的对象,并在函数内部对各个对象进行操作。
this指针是C++中一个非常重要的概念,也是面向对象编程中不可或缺的一部分。它可以让成员函数访问对象的成员变量和成员函数,避免命名冲突,返回对象本身的引用等。在使用this指针时,需要注意当前对象是否存在,以及函数内部是否正确处理对象的生命周期和有效性。