C++ 封装

一、封装

1、面向过程与面向对象

面向过程:面向过程是一件事“该怎么做”,是分析解决问题的步骤,然后用函数把这些步骤一步一步的实现,然后在使用的时候一一调用则可。

面向对象:面向对象是一件事“该谁来做”,然后那个“谁”就是对象。是以对象为核心,关注需要哪些对象,对象需要具备哪些功能,然后创建出解决问题的对象,利用对象调用相应的方法即可。采用OOP方法时,首先从用户的角度考虑对象,描述对象所需的数据以及描述用户与数据交互所需的操作。完成对接口的描述后,需要确定如何实现接口及存储数据。

面向过程的优缺点

优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源。比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展。

面向对象的优缺点

优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出高内聚低耦合的系统,使系统更加灵活、更加易于维护。
缺点:性能比面向过程低。

面向对象的精髓

面向对象的精髓在于封装,面向对象要求数据应该尽可能被封装,越多的东西被封装,就越少的人可以看到他,而越少的人可以看到他,我们就有越大的弹性去改变他。因此,越多的东西被封装,我们改变那些东西的能力就越大。这就是我们推崇封装的原因,他使我们改变事物而只影响有限客户。

2、抽象与类

生活中充满复杂性,处理复杂性的方法之一是简化与抽象。抽象就是将问题的本质提取出来,并根据特征来描述问题。C++中的类是一种将类转化为用户定义类型的C++工具,它将数据表示与操作数据的方法合成一个完整的包。类是对象的抽象,对象是类的具体化。

访问控制

C++提供了三个关键字public、private、protected,它们描述了对类成员的访问控制。

  • private:只能由该类中的函数或其友元函数访问。在类外不能访问,该类的对象也不能访问。
  • protected:可以被该类中的函数、子类的函数或其友元函数访问。在类外不能访问,该类的对象也不能访问。
  • public:可以被该类中的函数、子类的函数或其友元函数访问,也可以由该类的对象访问。

3、类的成员函数与成员变量

类可以看做是一种数据类型,它类似于普通的数据类型,但是又有别于普通的数据类型。类这种数据类型是一个包含成员变量和成员函数的集合。

成员变量

类的成员变量和普通变量一样,也有数据类型和名称,占用固定长度的内存。但是,在定义类的时候不能对成员变量赋值,因为类只是一种数据类型或者说是一种模板,本身不占用内存空间,而变量的值则需要内存来存储。

成员函数

类的成员函数也和普通函数一样,都有返回值和参数列表,它与一般函数的区别是:成员函数是一个类的成员,出现在类体中,它的作用范围由类来决定;而普通函数是独立的,作用范围是全局的,或位于某个命名空间内。

4、构造函数

下面提供了一个类的定义:

class Person{
private:
    string name;
    int age;
public:
    Person();
    Person(const string &name, int age);
    ~Person();
};

构造函数语法

构造函数名与类名相同,并且没有返回值,可以进行重载。

构造函数作用

创建类对象并对类对象的非静态数据成员进行初始化。

要注意的点

  1. 无法使用对象来调用构造函数,因为在构造函数构造出对象之前对象是不存在的。因此,构造函数被用来创建对象,而不能通过对象来调用。
  2. 如果没有提供任何构造函数,C++将提供默认的构造函数。它是默认构造函数的隐式版本,不做任何工作。如果为类提供了构造函数,此时编译器不会生成默认的构造函数。因此,为类定义了构造函数,就要为它提供默认的构造函数。
  3. 调用默认构造函数,不能使用圆括号
    Person zhangsan(); //定义了一个返回值类型为Person的方法
    Person lisi; //调用默认的构造函数
  4. 成员初始化列表只能应用于构造函数。

5、析构函数

析构函数语法

在类名前加~,没有返回值,也没有参数。

析构函数作用

当一个对象的生命周期结束时,程序会自动的调用一个特殊的成员函数–析构函数,来完成一些清理工作。如果程序员没有提供析构函数,编译器将隐式地声明一个默认的析构函数。

6、class与struct之间的区别

实际上,C++对结构体进行了扩展,使之具有与类相同的特征。它们之间的唯一的区别就是,结构的默认访问类型是public,而类的默认访问类型是private。C++程序员通常使用类来实现类描述,而把结构限制为只表示纯粹的数据对象。

7、this指针

什么是this指针?

this是指向实例化对象的一个指针,里面存储的是对象的地址,通过this可以访问内部的非静态成员变量与方法。每个非静态成员函数都有一个this指针(包括构造函数与析构函数),this指向调用对象。

this指针的作用

this的作用域是在类的内部,声明类时还不知道实例化对象的名字,所以使用this来使用对象。this指针指向用来调用成员函数的对象,在调用对象的非静态函数时,this作为隐藏的参数传给该方法。

obj.fun(1); 等价于 obj.fun(&obj, 1);

何时使用this指针?

  1. 在类的非静态成员函数中返回对象本身时,直接使用return *this。(常用于运算符重载、赋值构造函数、拷贝构造函数)
  2. 函数的形参名与成员变量名相同时`
Person::Person(const string &name, int age){
    this->name = name;
    this->age = age;
}

8、类作用域

在C++中引入了一种新的作用域即类作用域。在类中定义的名称的作用域(成员函数与数据成员)都为整个类,作用域为类的名称在类中是可见的,但是在类的外面是不可见的。因此,可以在不同的类中使用相同的名称,而不会引起冲突。类作用域意味着不能从类的外面直接访问类成员,公有的成员函数也是如此。也就是说,要调用公有的成员函数,必须通过类对象。同样,在定义成员函数时,必须使用作用域解析运算符。

8.1、声明作用域为类的常量

下面的声明方式是错误的:

class Person{
private:
    const int Months = 12;
    double cost[Months];
    string name;
    int age;

上面的做法是错误的,类声明只是描述了对象的形式,并没有创建对象。因此,将没有用于存储的空间。C++11提供了成员初始化,但是不适用于前面的数组声明。可以使用下面2种方式实现这个目标,效果相同。

方法一:在类中声明一个枚举

在类声明中声明的枚举的作用域为整个类,因此可以用枚举为整型常量提供作用域为整个类的符号名称。

class Person{
private:
    enum {Months = 12};
    double cost[Months];
    string name;
    int age;

使用上面的方式声明枚举并不会创建类数据成员,也就是说,所有对象中都不包含枚举。另外,Months只是一个符号名称,在作用域为整个类的代码中遇到它时,编译器将使用12来代替它。

方法二:使用static关键字

 class Person{
private:
    static const int Months = 12;
    double cost[Months];
    string name;
    int age;

上面创建一个名为Months的常量,该常量将与其它静态变量存储在一起,而不是存储在对象中。

9、类空间占用

下面的三个因素会影响一个类的大小:非静态成员变量、是否有虚函数、对齐方式

9.1、一个空类占用空间不为0

class A{
};

int main()
{
    cout << "sizeof(A) = " << sizeof(A) << endl;  // 输出结果:sizeof(A) = 1
    return 0;
}

为什么空的类什么都没有占用的内存大小是1呢?c++要求每个实例在内存中都有独一无二的地址。空类也会被实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了独一无二的地址了,所以空类的sizeof为1。

9.2、类的成员函数与成员方法空间占用

类成员变量

  • 普通的变量:是要占用内存的,但是要注意对齐原则(这点和 struct 类型很相似)
  • static 修饰的静态变量:不占用内容,原因是编译器将其放在全局变量区

类成员函数

  • 普通函数:成员函数的大小不在类的对象里面,同一个类的多个对象共享函数代码。而访问类的成员函数是通过类里面的一个指针实现,而这个指针指向的是一个table,table里面记录的各个成员函数的地址(当然不同的编译可能略有不同的实现),所以访问成员函数是间接获得地址的。当调用成员函数时会隐式的传入一个this指针,这样成员函数就知道具体是操作哪个对象。
  • 虚函数:有一个指向虚函数的指针,要占用 4 个字节,用来指定虚函数的虚拟函数表的入口地址。所以一个类的虚函数所占用的地址是不变的,和虚函数的个数是没有关系的
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在C++中,封装是面向对象编程中的一个重要概念。封装指的是将数据和对数据的操作封装在一起,形成一个类。这个类可以看作是一个黑盒子,外部只能通过类的接口来访问数据和操作,而无法直接访问类的内部实现。这样做的好处是可以隐藏类的内部实现细节,提高程序的安全性和可维护性。 在C++中,封装主要通过访问控制权限来实现。C++提供了三种访问控制权限:public、protected和private。public成员可以在任何地方访问,protected成员可以被派生类访问,private成员只能在本类中访问。 一个类中的数据成员通常被声明为private,而对外提供一些公共的成员函数来访问和修改这些数据成员。这些公共的成员函数就是类的接口。通过这样的封装,可以有效地控制数据的访问和修改,从而保证程序的正确性和安全性。 下面是一个简单的示例代码,演示了如何在C++中封装一个类: ```cpp class MyClass { public: MyClass(); // 构造函数 void setData(int data); // 设置数据 int getData(); // 获取数据 private: int m_data; // 数据成员 }; MyClass::MyClass() { m_data = 0; } void MyClass::setData(int data) { m_data = data; } int MyClass::getData() { return m_data; } int main() { MyClass obj; obj.setData(100); cout << obj.getData() << endl; return 0; } ``` 在上面的代码中,MyClass类中的数据成员m_data被声明为private,而对外提供了两个公共的成员函数setData和getData,用于设置和获取数据成员。这样,外部代码就无法直接访问和修改m_data成员,必须通过MyClass类的接口来访问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值