C++ 类和对象

面向对象

是一种程序设计范型,同时也是一种程序开发的方法。对象指的是类的实例,将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性,灵活性和扩展性。把一组数据结构和处理他们的方法组成对象,把相同行为的对象归纳为类,通过对类的封装隐藏内部细节,通过继承实现类的特化/泛化,通过多态实现基于对象类型的动态分派。

C++不是纯面向对象语言,而是基于面向对象的语言。因为它包含C的部分,而C是面向过程的。
面向对象的三大特征:封装、继承、多态。

类的大小

  1. 为类的非静态成员数据的类型大小之和。
  2. 有编译器额外加入的成员变量的大小,用来支持语言的某些特性(如:指向虚函数的指针)
  3. 为了优化存取效率,进行的边缘调整。
  4. 与类中的构造函数,析构函数以及其他成员函数无关。

类的大小与它当中的构造函数,析构函数,以及其他的成员函数无关,只与它当中的成员变量有关。需考虑内存对齐。

为什么要内存对齐

内存对齐主要存在于struct和union等复合结构在内存中的分布情况,许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们要求这些数据的首地址的值是某个数(通常是4或8);对于内存对齐,主要是为了提高程序的性能,数据结构,特别是栈,应尽可能在自然边界上 对齐,经过对齐后,cpu的内存访问速度大大提升。
简单来说,就是以空间换时间

内存对齐的计算

1.第一个成员在与结构体变量偏移量为0的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数=编译器默认的一个对齐数与该成员大小的较小值
VS中默认的值是8
gcc中默认的值是4
3.结构体总大小为最大对齐数(每个成员变量出了第一个成员都有一个对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

空类的计算

空类的大小为1。表示这个类型的对象存在过(用来占位)。

隐含的this指针

  1. 每个成员函数都有一个指针形参。它存在栈里,它的名字是固定的,称为this指针,this指针是隐式的。(构造函数比较特殊,没有这个隐含的this指针)。this指针存在ecx寄存器中,方便更快的调用。
  2. 编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参this指针。
  3. this指针是成员函数隐含指针形参,是编译器自己处理的,我们不能在成员函数的形参中添加this指针的参数定义,也不能在调用时显示传递对象的地址给this指针。

类的4个默认成员函数

1.构造函数
类的构造函数是一种特殊的成员函数。主要用来在创建对象时初始化对象,即为成员变量赋初始值。

成员变量为私有的,要对他们进行初始化,必须用一个共有成员函数来进行。同时这个函数应该有且仅在定义对象时自动执行一次,这时调用的函数称为构造函数。

特征:
a.函数名与类名相同。
b.无返回值
c.对象构造(对象实例化)时系统自动调用对应的构造函数。
d.构造函数可以在类中定义,也可以在类外定义。
e.如果类定义中没有给出构造函数,则C++编译器自动产生一个缺省的构造函数,但只要我们定义了一个构造函数,系统就不会生成缺省的构造函数。
f.无参的构造函数和全缺省值的构造函数都认为是缺省构造函数,并且缺省的构造函数只能有一个。

  • 无参构造函数和带参构造函数
class Date
{
public:
    //1.无参的构造函数
    Date()
    {
        _year = 1900;
        _month = 1;
        _day = 1;
    }
    //Date();
    void Display()
    {
        cout << this->_year << "-" << this->_month << "-" << this->_day<<endl;
    }
    void SetDate(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
private:
    int _year;
    int _month;
    int _day;
};

//Date::Date()  
//{
//  _year = 1900;
//  _month = 1;
//  _day = 1;
//}
void TestDate()
{
    Date d1;  //调用无参构造函数
    d1.Display();
    d1.SetDate(2017, 1, 1);
    d1.Display();
}

Date::Date() 缺省构造参数声明与定义分离,可以在声明或定义中给默认参数。在类外调用构造函数,与在类内调用构造函数结果是一致的。
这里写图片描述

  • 带参的构造函数
class Date
{
public:
    //2.带参的构造函数
    Date(int year,int month,int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void Display()
    {
        cout << this->_year << "-" << this->_month << "-" << this->_day<<endl;
    }
    void SetDate(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
private:
    int _year;
    int _month;
    int _day;
};
void TestDate()
{
    Date d2(2018, 1, 1);//调用带参的构造函数
    Date d3();//注意这里没有调用d3的构造函数定义出d3

    d2.Display();
    //d3.Display();

    d2.SetDate(2017, 2, 1);
    d2.Display();
}

这里写图片描述

  • 带缺省参数的构造函数
class Date
{
public:
    //3.缺省的构造函数
    Date(int year = 2000,int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void Display()
    {
        cout << this->_year << "-" << this->_month << "-" << this->_day<<endl;
    }
    void SetDate(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
private:
    int _year;
    int _month;
    int _day;
};

void TestDate()
{
    Date d4;
    Date d7(2013, 4, 1);
    d4.Display();
    d7.Display();
}

无参构造函数不用传参,带缺省参数的构造函数也不用传参,因此无参相当于带缺省参数,调用时只需用一个即可。
这里写图片描述

  • 半缺省构造函数
class Date
{
public:
    //4.半缺省的构造函数
    Date(int year, int month = 2, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void Display()
    {
        cout << this->_year << "-" << this->_month << "-" << this->_day<<endl;
    }
    void SetDate(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
private:
    int _year;
    int _month;
    int _day;
};

void TestDate()
{
    Date d5(2011);
    Date d6(2011,3,6);

    d5.Display();
    d6.Display();
}

这里写图片描述
2.拷贝构造函数
创建对象时使用同类对象来进行初始化,这时所用的构造函数被称为拷贝构造函数,拷贝构造函数是特殊的构造函数。
特征:

  1. 拷贝构造函数其实是一个构造函数的重载。
  2. 拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归。传值参数,把形参复制到实参会调用拷贝构造函数。如果允许拷贝构造函数传值,就会在拷贝构造函数内调用拷贝构造函数,就会形成无休止的递归周期从而导致栈溢出。
  3. 若未显示定义,系统会默认缺省的拷贝构造函数。缺省的拷贝构造函数会依次拷贝成员进行初始化。
class Date
{
public:
    Date()
    {
        _year = 1900;
        _month = 1;
        _day = 1;
    }
    Date(const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
        //1.这里的对象可以直接访问私有的成员变量,在类的成员变量函数中可以直接访问同类对象的私有/保护成员。
        //2.C++访问限定符是以类为单位的,也就是说在这个单位内的成员可以互相访问
    }
    void Display()
    {
        cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};
void TestDate2()
{
    Date d1;
    d1.Display();
    Date d2(d1);
    d2.Display();
    Date d3 = d1;
    d3.Display();
}

这里写图片描述
3.析构函数
当一个对象的生命周期结束时,C++编译系统会自动调用一个成员函数,这个特殊的成员函数即析构函数。析构函数是特殊的成员函数,其特征如下:
a.析构函数在类名前加上字符~。
b.析构函数无参数无返回值。
c.一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数。
d.对象生命周期结束时,C++编译系统自动调用析构函数。
e.注意析构函数体内并不是删除对象,而是做一些清理工作
f.如果先构造的V1,后构造V2。那么析构的顺序应该是:先析构V2,再析构V1。(在栈上)


class Date()
{
public:
    Date(int year = 2000,int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    //析构函数:做清理工作
    ~Date()
    {
        cout << "~Data()" << endl;
    }
private:
    int _year;
    int _month;
    int _day;
}

这里的析构函数需要完成清理工作

class MyVector
{
public:
    MyVector(size_t capacity = 0)
    {
        if (capacity == 0)
        {
            _a = NULL;
            _size = 0;
            _capacity = 0;
        }
        else
        {
            _a = (int*)malloc(capacity*sizeof(int));
            _size = 0;
            _capacity = capacity;
        }
    }
    ~MyVector()
    {
        if (_a)
        {
            free(_a);
            _a = NULL;
        }
        _size = _capacity = 0;
    }
private:
    int* _a;
    size_t _size;
    size_t _capacity;
};

4.赋值运算符重载
拷贝构造函数是创建的对象,使用一个已有对象来初始化这个准备创建的对象。赋值运算符的重载是对一个已存在的对象进行拷贝赋值。让自定义类型像内置类型一样使用。
特征:
a.operator+合法的运算符构成函数名
b.重载运算符以后,不能改变运算符的优先级/结合性/操作数个数。

class Date
{
public:
    Date()
    {}
    //拷贝构造函数
    Date(const Date&d)
        :_year(d._year)
        ,_month(d._month)
        , _day(d._day)
    {}
    Date& operator=(const Date& d)
    {
        if (this != &d)
        {
            this->_year = d._year;
            this->_month = d._month;
            this->_day = d._day;
        }
        return *this;
    }
    void Display()
        {
            cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
        }
private:
    int _year = 1900;
    int _month = 1;
    int _day = 1;
};

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值