类和对象(上)

1类的定义

clss classname{

成员变量;

成员函数;

};

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分 号不能省略。

其实我们的类和我们的结构体很相似的,都属于自定义类型。在c++中我也可以用struct来定义我们的类,和class是一样的用法。

类的两种定义方法: 

第一种就是:

成员函数在类体里面实现,那么我们的成员函数就有可能当成内联函数。

第二种就是

在我们的头文件中包含我们的类声明,我们的类里面只放成员函数的声明,我们里面的成员函数的实现是在我们的.cpp文件中,这个时候我们的成员函数就需要我们使用我们     ::    操作符。

在我们的成员函数名字前面加一个类名::

2.类的访问限定符及封装、

访问限定符:

C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选 择性的将其接口提供给外部的用户使用。

访问限定符号有:private,public,protected。

公有public是可以下我们的类外访问的 。

保护protected和私有private是不能直接在类外面直接访问的。

作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。

我们的访问限定符号作用域是在遇到相爱一个访问限定符截止。

如果后面直接没有相爱一个限定符,就只一直到我们的类的最后。

如果没有写限定符,那么默认的是:struct定义的类默认的是public,class定义的类默认的是private。

C++中struct和class的区别是什么?

C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来 定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类 默认访问权限是private。

封装:

我们的c++是一种面向对象的一种语言,而面向对象的语言有三大特点:封装,继承,多态。

类和对象主要是对封装的学习。

什么是封装:

数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来 和对象进行交互。

封装是一种管理,让我们更方便使用类。对于电脑这样一个复杂的设备,提供给用 户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日 常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。我们不在乎里面的是怎么实现的,计 算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以 及键盘插孔等,让用户可以与计算机进行交互即可。

3.类的作用域

类的作用域叫做类域,类里面的成员是都是在类域里面的,在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。

class Person
{
public:
 void PrintPersonInfo();
private:
 char _name[20];
 char _gender[3];
 int  _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
 cout << _name << " "<< _gender << " " << _age << endl; 
}

4.类的实例化


用类类型创建对象的过程,称为类的实例化:类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没 有分配实际的内存空间来存储它

在C++中,类的实例化是通过创建类的对象(也称为类的实例)来完成的。这通常涉及到使用类的构造函数来初始化新创建的对象。构造函数是一个特殊的成员函数,它在创建对象时自动调用,用于初始化对象的成员变量。

下面是一个C++类实例化的简单示例:cpp

#include <iostream>
using namespace std;

// 定义一个类
class Point {
private:
    int x, y; // 类的私有成员变量

public:
    // 构造函数
    Point(int a, int b) {
        x = a;
        y = b;
    }

    // 成员函数,用于显示点的坐标
    void display() {
        cout << "(" << x << ", " << y << ")" << endl;
    }
};

int main() {
    // 类的实例化
    Point p1(10, 20); // 调用构造函数创建Point类的对象p1

    // 调用成员函数
    p1.display(); // 输出: (10, 20)

    // 可以创建多个对象
    Point p2(30, 40);
    p2.display(); // 输出: (30, 40)

    return 0;
}

类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图

在这个例子中,Point类有两个私有成员变量x和y,它们分别表示点的横坐标和纵坐标。Point类还有一个构造函数,它接受两个整数参数并将它们分别赋值给x和y。此外,Point类还有一个display成员函数,用于输出点的坐标。

在main函数中,我们通过调用Point类的构造函数并传递相应的参数来创建了两个Point类的对象p1和p2。然后,我们通过调用这些对象的display成员函数来显示它们的坐标。

注意,在C++中,类的实例化是通过使用类的名称和圆括号(其中可以包含传递给构造函数的参数)来完成的。实例化过程会调用构造函数来初始化新创建的对象。如果类没有显式定义构造函数,编译器会提供一个默认的构造函数,但它不会为类的成员变量提供任何初始化操作(对于内置类型成员变量,其初始值是未定义的)。然而,在C++11及更高版本中,可以使用初始化列表或默认成员初始化器来为成员变量提供默认初始化值。

一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量

 

5.类的对象大小的计算

类的大小的计算和我们的结构体的大小的计算差不多,因为我们的类里面并不存储我们的成员函数。

在C++中,类的大小计算是一个相对复杂的过程,因为它不仅涉及到类中声明的成员变量的大小,还受到多种其他因素的影响,如内存对齐、虚函数、继承等。以下是对类大小计算的一些关键点和步骤的详细解释:

1. 成员变量的大小


类的大小至少等于其所有非静态数据成员大小之和。这是因为静态成员变量不占用类对象的存储空间,而是被所有对象共享。
如果类中包含非静态成员变量,那么这些变量的大小将直接影响类的大小。


2. 内存对齐


为了提高内存访问效率,编译器通常会对成员变量进行内存对齐。这意味着每个成员变量的起始地址都是某个特定值(如4或8,取决于编译器和平台)的整数倍。
内存对齐可能导致类的大小比其成员变量大小之和更大。例如,如果一个类中有一个char类型的成员(占用1字节)和一个int类型的成员(在32位机器上占用4字节),但由于内存对齐,这个类的大小可能会是8字节而不是5字节。


3. 空类的大小


根据C++标准,空类的大小不为0,而是至少为1字节。这是为了确保空类的不同对象在内存中有唯一的地址。


4. 计算示例

假设有以下类定义:

//cpp
//复制
class Base {
public:
void fun();
    int a;
    char b;
};

class Derived : public Base {
    double c;
};

Base类一个int类型成员(4字节)和一个char类型成员(1字节)。由于内存对齐,Base类的大小可能是12字节(具体取决于编译器的对齐策略)。
Derived类继承自Base类,并添加了一个double类型成员(8字节)。因此,Derived类的大小将包括Base类的大小(假设为12字节)、double类型成员的大小(8字节)以及可能的额外对齐填充。
7. 结论

类的大小计算是一个涉及多个因素的过程,包括成员变量的大小、内存对齐等。在实际编程中,了解这些因素对于优化类的内存布局和性能至关重要。

请注意,上述解释和示例基于一般性的C++规则,并可能因编译器和平台的不同而有所差异。在实际应用中,最好通过编译器提供的工具(如sizeof运算符)来检查类的大小。

6.类成员函数的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;
 }
}

 C++编译器给每个“非静态的成员函数“增加了一个隐藏 的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量” 的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编 译器自动完成。

它是类的一个隐含成员,指向调用成员函数的对象本身。每个成员函数(非静态成员函数)都有一个 this 指针,它不需要显式声明,但在成员函数内部可以隐式地访问它。

this指针的特性

1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。

2. 只能在“成员函数”的内部使用

3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给 this形参。所以对象中不存储this指针。

4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传 递,不需要用户传递。

this 指针的作用

访问类的成员:通过 this 指针,可以访问类的成员变量和成员函数。尽管在大多数情况下,我们可以直接访问类的成员而不需要显式地使用 this 指针,但在某些特定情况下(如成员函数内部定义的其他函数或类成员初始化列表中),this 指针就显得尤为重要。

返回当前对象:在某些情况下,成员函数需要返回调用它的对象本身(比如链式调用或拷贝赋值操作符的实现中)。此时,可以使用 return *this; 来返回调用该成员函数的对象的引用

区分成员变量和局部变量:如果成员函数的参数名与类的成员变量名相同,那么在函数体内成员变量将被隐藏(名称隐藏)。此时,可以使用 this->成员变量名 来访问类的成员变量。

两个题目:

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行

他的汇编:

 

 这个程序是可以正常运行的,我们的this指针是可以指向空指针的,首先我们的类里面的成员函数不存在我们的类变量里面,所以我们的类的大小是不用算成员函数大小的,而我们的成员函数是存在一个公共的区域,p->Print(),换到我们的汇编语言中是没有进行解引用的,所以就不会程序崩溃。

而这里的程序会崩溃,因为我们的_a是存在类变量里面的,我们地去打印_a,其实隐含了一个this指针,实际上是this->_a,这就是会对我们的空指针解引用。

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-13 03:36:03       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-13 03:36:03       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-13 03:36:03       58 阅读
  4. Python语言-面向对象

    2024-07-13 03:36:03       69 阅读

热门阅读

  1. docker/podman 安装nacos

    2024-07-13 03:36:03       23 阅读
  2. 【面试题】MySQL(第三篇)

    2024-07-13 03:36:03       18 阅读
  3. 腾讯面试:let、const解决了什么问题?

    2024-07-13 03:36:03       18 阅读
  4. 并查集

    并查集

    2024-07-13 03:36:03      16 阅读
  5. python多线程与多进程开发实践及填坑记(3)

    2024-07-13 03:36:03       26 阅读