面向对象编程语言(C++)复习笔记(3)——类与对象

一,类的概念及封装

1.什么是封装

第一层含义:封装是面向对象程序设计最基本的特性。把数据(属性)和函数(方法)合成一个整体,这在计算机世界中是用类和对象实现的。(把属性和方法进行封装
第二层含义:把客观事物封装成抽象的类,并且类可以把自己的属性和方法只让可信的类或者对象操作,对不可信的进行信息的隐藏。(对属性和方法进行访问控制

2.类的访问控制

  在C++中可以对类的属性和方法定义访问级别,public修饰的属性和方法,可以在类的内部访问,也可以在类的外部进行访问。private修饰的属性和方法,只能在类的内部进行访问。

3.类的意义

类是把属性和方法进行封装,同时对类的属性和方法进行访问控制。
类是由我们根据客观事物抽象而成,形成一类事物,然后用类去定义对象,形成这类事物的具体个体。
类是一个数据类型,类是抽象的,而对象是一个具体的变量,是占用内存空间的。

二,构造函数

1.前言

当我们创建一个对象的时候,常常需要做某些初始化操作,例如属性进行赋初值。为了解决这个问题,C++编译器提供了构造函数来处理对象的初始化。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在创建对象时自动执行。

2.构造函数的定义及调用方式

定义:C++类中可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数。构造函数可以有参数列表,但是不能有函数返回值。
调用:一般情况下C++编译器会自动调用构造函数,但在一些情况下则需要手工调用构造方法。

3.构造函数的分类

  • 无参构造函数:构造函数没有函数参数。
  • 有参构造函数:构造函数有函数参数。
  • 拷贝构造函数:构造函数的参数为const ClassName &vriable。

4.拷贝构造函数示例及调用

拷贝构造函数的调用方式

  • 当用对象1初始化对象2的时候拷贝构造函数被调用。
  • 当用对象1括号方式初始化对象2的时候拷贝函数被调用。
  • 当用对象(此处是元素而而非指针或引用)做函数参数的时候,实参传递给形参的过程会调用拷贝构造函数。
  • 当用对象(此处是元素而而非指针或引用)做函数返回值的时候,若用同一类型来接收该返回值,则会执行拷贝构造函数(此时会涉及到匿名对象的去和留的问题,因为函数的返回对象是个匿名对象)。
# define _CRT_SECURE_NO_WARNINGS
# include<iostream>

using namespace std;

class Student
{
private:
    char * name;
    int age;
public:
    Student(char * name, int age)
    {
        /* 在构造函数中分配堆内存 */
        this->name = (char *)malloc(sizeof(name)+1);
        /* 初始化成员变量 */
        strcpy(this->name, name);
        this->age = age;
        cout << "构造函数被执行..." << endl;
    }
    ~Student()
    {
        if (this->name != NULL)
        {
            /* 在析构函数中释放堆内存 */
            free(this->name);
            this->name = NULL;
            this->age = 0;
        }
        cout << "析构函数被执行..." << endl;
    }
    void print()
    {
        cout << this->name << " = " << this->age << endl;
    }
    
};

int main()
{
    Student stu("王刚", 21);
    stu.print();
    return 0;
}

5.拷贝构造函数的深拷贝和浅拷贝

a.拷贝构造函数的执行原理

  拷贝构造函数有四种调用方式,当我们不写拷贝构造函数的时候,C++默认的帮我们写了拷贝构造函数,其函数体实现的是简单的成员变量的值的拷贝。这种默认的拷贝方式,我们称为浅拷贝,即只是简单的复制成员变量的值到另一个对象中。

b.深拷贝问题的抛出

  当我们的成员变量是动态分配的一组堆内存的时候,这个时候C++默认的为我们写的拷贝构造函数把成员变量的值进行了复制,那么副本也会指向原始对象所分配的堆内存,当我们在析构函数中释放堆内存的时候,就会发生两次析构现象(一次是原始对象释放堆内存,一次是副本对象释放堆内存)因此造成程序崩溃。

6.构造函数小结 

当类中没有定义任何构造函数的时候,C++编译器默认为我们定义一个无参构造函数,函数体为空。
当在类中定义任意构造函数后(包括拷贝构造函数),C++编译器就不会为我们定义默认构造函数,也就是说我们定义了构造函数就必须要使用。
C++默认提供拷贝构造函数是指向的浅拷贝。只是进行了简单的成员变量的值的复制。
拷贝构造函数只有当我们写了以后C++编译器才不会给我提供,如若不写则C++编译器会一直提供默认的拷贝构造函数。

三,对象的动态建立和释放

1.什么是对象的动态建立和释放

  通常我们创建的对象都是由C++编译器为我们在栈内存中创建的,我们无法对其进行生命周期的管理。所以我们需要动态的去建立该对象,因此我们需要在堆内存中创建对象和释放对象。在C语言中为我们提供了malloc()函数和free()函数来为我们提供在堆内存中分配变量的方式,但是在C++中引入了new和delete关键字来让我们动态的创建和释放变量。

2.new和delete关键字

new关键字是用来在堆内存中创建变量的,格式为:Type * ptr = new Type(常量/表达式); 其参数列表中的常量/表达式可以用来给变量初始化,也可以省略不写。其返回结果为该类型的指针。如果内存分配失败则返回空指针。
delete关键字是用来释放用new关键字创建的内存,格式为delete ptr(释放数组必须需要加中括号,delete [] ptr)。

3.new和delete关键字与malloc和free的区别

new关键字在分配内存的时候,会根据其创建的参数调用相应的类的构造函数。delete关键字会在释放内存之前,会首先调用类的析构函数释放对象中定义的内存。
malloc和free关键字不会去调用类的构造函数和析构函数。

四,对象的动态建立和释放

1.什么是对象的动态建立和释放

  通常我们创建的对象都是由C++编译器为我们在栈内存中创建的,我们无法对其进行生命周期的管理。所以我们需要动态的去建立该对象,因此我们需要在堆内存中创建对象和释放对象。在C语言中为我们提供了malloc()函数和free()函数来为我们提供在堆内存中分配变量的方式,但是在C++中引入了new和delete关键字来让我们动态的创建和释放变量。

2.new和delete关键字

new关键字是用来在堆内存中创建变量的,格式为:Type * ptr = new Type(常量/表达式); 其参数列表中的常量/表达式可以用来给变量初始化,也可以省略不写。其返回结果为该类型的指针。如果内存分配失败则返回空指针。
delete关键字是用来释放用new关键字创建的内存,格式为delete ptr(释放数组必须需要加中括号,delete [] ptr)。

3.new和delete关键字与malloc和free的区别

new关键字在分配内存的时候,会根据其创建的参数调用相应的类的构造函数。delete关键字会在释放内存之前,会首先调用类的析构函数释放对象中定义的内存。
malloc和free关键字不会去调用类的构造函数和析构函数。


五,静态成员变量和静态成员函数

1.static关键字

  static关键字用来声明类中的成员为静态属性。当用static关键字修饰成员后,该类所创建的对象共享static成员。无论创建了多少个对象,该成员只有一份实例。静态成员是与类相关的,是类的一种行为,而不是与该类的对象相关。

2.静态成员的概念

  静态成员是类所有的对象的共享成员,而不是某个对象的成员,它在对象中不占用存储空间,这个成员属于整个类,而不属于具体的一个对象,所以静态成员变量无法在类的内部进行初始化,必须在类的外部进行初始化。比如定义一个学生类,那么学生对象总数可以声明为static,在构造方法中,对该变量进行加1,从而统计学生对象的数量。

3.静态成员变量总结

  • 静态成员变量可以用static关键字定义,但是初始化必须在类的外面进行初始化。
  • 静态成员变量可以被类及类的对象所访问和修改。
  • 静态成员变量遵循类的访问控制原则,如果为private修饰,则只可以在类的内部和在类外面初始化的时候访问,不会再被其他方式访问。

4.静态成员函数总结

静态成员函数用static关键字定义,在静态成员函数中可以访问静态成员变量和静态成员函数,但不允许访问普通的成员变量和成员函数,因为普通的成员属于对象而不属于类。层次不一样。但是在普通成员中可以访问静态成员。
当静态成员函数在类中定义,但是在类的外面实现的时候,不需要再加static关键字。
静态成员函数没有this指针。

5.静态成员重点归纳

  • 静态成员是类和类的对象的所有者,因此静态成员变量不能在类的内部进行初始化,必须在类的外部进行初始化。
  • 静态成员依旧遵循private,protected,public的访问控制原则。
  • 静态成员函数中没有this指针,不能访问普通的成员变量和成员函数,可以访问静态成员变量和成员函数,但是可以通过传递对象的方式访问普通成员。
# include<iostream>

using namespace std;

class MyStudent
{
private:
    static int count;/* 学生对象总数 */
    char name[64];
    int age;
public:
    static int n;
public:
    MyStudent(char * name,int age)
    {
        strcpy(this->name, name);
        this->age = age;
        MyStudent::count++;/* 学生数量加1 */
    }
    void getCount()/* 普通成员函数访问静态成员变量 */
    {
        cout << "学生总数:" << MyStudent::count << endl;
    }
};
/* 静态成员变量初始化 */
int MyStudent::count = 0;
int MyStudent::n = 10;

int main()
{
    /* 测试静态成员变量 */
    MyStudent student1("王刚",22);
    student1.getCount();
    /* 对象和类方式访问静态成员变量 */
    student1.n = 100;
    MyStudent::n = 200;
}


六,友元函数和友元类

1.友元函数

  当我们定义类的时候,使用private关键字修饰成员变量(成员函数),这样做到了访问控制。有些时候,我们需要让一些函数来访问对象的私有成员(属性或方法),C++为我们提供了友元函数这个概念,所谓的友元函数就是指这个函数是这个类的好朋友,允许让这个函数访问这个类创建的对象的私有属性和私有方法。友元函数用friend函数来声明,友元函数的声明必须在类的内部,友元函数的实现必须要在类的外部(如果友元函数的实现也在内部,那还要用友元函数干什么?),友元函数的声明位置与访问控制符无关。

# include<iostream>
using namespace std;

/* 定义点类 */
class Point
{
private:
    int x;
    int y;
    /* 友元函数的定义:求两点的距离 */
    friend int distance(Point &p1, Point &p2);
public:
    Point(int x, int y)
    {
        this->x = x;
        this->y = y;
    }
};
/* 友元函数的实现 */
int distance(Point &p1, Point &p2)
{
    int dx = p1.x - p2.x;
    int dy = p1.y - p2.y;
    return sqrt(dx*dx + dy*dy);
}

int main()
{
    Point p1(3, 4);
    Point p2(0, 0);
    int dis = distance(p1, p2);
    cout << "点(3,4)到原点的距离为:" << dis << endl;
}

2.友元类

若B类是A类的友元类,则B类的所有成员函数都是A类的友元函数。类B可以访问类A的所有私有属性和方法。
友元类通常被设计为一种对数据操作或者类之间传递消息的辅助类。

# include<iostream>
using namespace std;

/* 定义类A */
class A
{
private:
    int x;
    friend class B;/* 定义类B为类A的友元类 */
private:
    void setX(int x)
    {
        this->x = x;
    }
};

/* 定义类B */
class B
{
private:
    A AObj;
public:
    /* 类B的所有成员函数都是类A的友元函数,因此都可以访问类A的私有属性和方法 */
    void operater(int tmp)
    {
        AObj.setX(tmp);
    }
    void display()
    {
        cout << "类A的私有属性x = " << AObj.x << endl;
    }
};

int main()
{
    B b;
    b.operater(100);
    b.display();

    return 0;
}

七,C++中的类与对象模型

C/C++内存模型

1.内存模型分类

  • 栈区:由编译器自动分配和释放,用来存放函数的参数,局部变量。存放在栈中的数据只在当前函数及下一层函数中有效,函数一旦结束,这些数据就被释放了。
  • 堆区:由程序员分配和释放,如果程序员没有释放则在程序结束时由OS释放,由于忘记释放通常会出现内存泄露问题。
  • 全局(静态)区:用来存储全局变量和静态变量,程序结束时由OS释放。
  • 常量区:存放字面量,不允许修改,如字符串"Hello,Unreal",程序结束时由OS释放。
  • 代码区:存放代码(如函数),不允许修改(类似常量区),但可以执行(不同于常量区)。

2.理解函数,代码和内存分配的问题

  函数是由一条条的操作指令(代码)组合而成的,其指令可以是定义变量向内存申请空间,或者是操作指令,例如两数相加。

C++中类和对象的内存分配模型

1.C++中类和对象

  在C语言中,“数据”和“函数”是分开来声明的,也就是说,语言并没有支持“语言和函数”之间的关联性。但是在C++中却提供了“类”来将“数据”和“函数”绑定在了一起。C++中的类由成员变量,静态成员变量,构造函数,成员函数,静态函数和虚函数组成。下面我们来研究组成类的数据和函数是如何在内存中分配的。

2.C++中是如何管理类和对象的  我们首先来定义几个类,然后对下面的代码进行分析。

# include<iostream>
using namespace std;
/* 定义第一个类:12个字节 */
class C1
{
private:
    int a;// 4
    int b;// 4
    int c;// 4
};
/* 定义第二个类:12个字节 */
class C2
{
private:
    int a;// 4
    int b;// 4
    int c;// 4
    static int d;// 0
};
/* 定义第三个类:4个字节 */
class C3
{
private:
    int a;// 4
    static int b;// 0
public:
    void setA(int a)// 0
    {
        this->a = a;
    }
    int getA()// 0
    {
        return this->a;
    }
    static void add()// 0
    {
        C3::b += 100;
    }
};
/* 定义结构体:4个字节 */
struct S1
{
    int a;
    static int b;
};

int main()
{
    cout << "C1类所占字节数:" << sizeof(C1) << endl;
    cout << "C2类所占字节数:" << sizeof(C2) << endl;
    cout << "C3类所占字节数:" << sizeof(C3) << endl;
    cout << "S1结构体所占字节数:" << sizeof(S1) << endl;

    return 0;
}

 3.上述代码结果分析

  • C++中的成员变量和成员函数是分开存储的。
  • 普通的成员变量存放在对象(变量)中,与结构体变量有相同的内存布局和字节对齐方式。
  • 静态成员变量存储在全局(静态)区。
  • 成员函数和静态函数存储在代码区。


4.C++成员变量存放在对象中,那么成员函数是如何管理的?


 5.上述代码总结

C++的类是用结构体实现的,因此类中的成员变量和结构体里的变量内存布局方式一致。
C++类中的成员函数隐式包含一个指向当前对象的指针,哪个对象调用该函数,C++编译器就默认将该对象包含在函数中。
静态成员变量和静态成员函数属于类,因此静态成员函数不包含指向具体对象的指针。

6.this指针

通过上述示例,我们发现类中的普通成员函数包含一个this指针,该指针指向调用当前函数的对象,即谁调用我,我就指向谁。
this指针通常用于在成员函数或者构造函数的形参和成员变量一致的时候,用this指针来表示当前对象的成员变量,形参直接赋值给成员变量即可。

文章内容来源于:

C++中的类与对象模型 - MetalSteel - 博客园

C++中的类和对象(二) - MetalSteel - 博客园

C++中的类和对象(一) - MetalSteel - 博客园

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

li_wen01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值