【C++】类和对象

1.C++语言的的作用域:

    局部作用域;

    全局作用域;

    类作用域;

2.在《C++Primer》中是这样解释类的:

    类类型通常被称为抽象数据类型(abstract data type)。抽象数据类型将数据(即状态)和作用于状态的操作视为一个单元,我们可以抽象地考虑类该做什么,而无需知道类如何完成这些操作。

    抽象数据类型是面向对象编程和泛型编程的基础。

3.面向对象语言的四大特征:抽象  封装/隐藏  继承  多态

   面向对象语言的三大特征:封装/隐藏  继承 多态

4.定义一个类:

    (1)简言之,类就是定义了一个新的类型和新的作用域

    (2)创建类的目的,是用来抽象实体的类型,从实体的属性和行为两方面,得到类的成员变量和成员方法,因为类是实体类型的抽象,所以类类型不占空间。

    (3)每个类可以没有成员,也可以定义多个成员,成员可以是数据,函数或类型别名。

    (4)一个类可以包含若干个public(公有的),private(私有的),protected(受保护的)部分/

    (5)用struct和class都可以定义类,但是,在没有访问限定符的情况下,class定义的类默认都是private访问限定,struct都是public访问限定。

    (6)对于类成员方法的定义,可以在类内定义,也可以在类外定义,但是要注意的是,如果在类外定义,需要在函数名之前加上类作用域。如果成员方法在类体内定义,就自动处理为inline函数了。

        普通成员变量和成员方法需要依赖对象才可以调用。

5.构造函数和析构函数:

    (1)构造函数:构造函数是一个特殊的、与类名相同的成员函数,主要用于对对象内存进行初始化

    (2)析构函数:析构函数是类名前加~的成员函数,主要作用是对象内存释放前,把占用的其它系统资源释放掉。

    值得注意:对象的内存是由系统开辟的,构造函数只是做了内存的初始化,构造函数调用完了,我们就可以理解为对象产生了。所以构造函数不是给对象开辟内存的。同理,析构函数是在对象内存释放之前,将对象占用的外部资源先释放掉(例如释放堆上占用的内存)。所以析构函数也不是释放对象内存的

    (3)构造函数和析构函数没有返回值。

    (4)构造函数可以带参数,所以构造函数是可以重载的。析构函数不能带有参数,所以析构函数只能有一个。

    (5)当类中没有任何构造/析构函数的时候,编译器会给当前类一个默认的构造/析构函数。(是一个空函数,什么都不做)

    (6)如果类中定义了任意构造函数,就不会产生默认构造函数了。

    (7)在定义默认构造的对象,不要在构造时在对象名字后面加()。

    (8)先构造的函数是后析构的,后构造的函数是先析构的。

6.一个简单的类

#include<iostream>
#include<typeinfo>
using namespace std;

class CPerson//类名
{
public://共有的
    /*构造函数 名字与类名相同 可以任意带参*/
    CPerson()
    {
        age = 20;
        strcpy(name,"LiMing");
        strcpy(address,"BeiJing");
    }
    CPerson(int ag,char *na,char *add)
    {
        age = ag;
        strcpy(name,na);
        strcpy(address,add);
    }
    void show();
    ~CPerson(){}
private://私有的
    int age;
    char name[20];
    char address[20];
};
void CPerson::show()// 在类外定义函数需要注明作用域
{
    cout<<"年龄:"<<age<<" 名字:"<<name<<" 地址:"<<address<<endl;
}
int main()
{
    CPerson people1(20,"jack","Xi'An");
    people1.show();//调用成员方法需要依赖对象
    CPerson LM;//定义默认构造的对象,不要在对象后加()
    LM.show();
}

7.成员函数被const修饰:

    将关键字const加在形参之后,就可以将成员函数声明为常量。const成员不能改变其操作的对象的数据成员。const必须同时出现在声明和定义中,若只出现一处,就会出现编译时的错误

    若提供返回名字和地址的操作,函数应该为const的,这样提高了方法的安全性,防止被修改。

8.this指针

    一个类可以产生多个成员对象,每个对象都有自己的一份成员变量,它们共享这个成员的成员方法。那么问题来了,即然类的方法是由多个对象共享的,那么成员方法在被调用的时候是怎么知道是哪个对象调用的呢?

    这个时候就引入this指针的概念了,成员函数具有一个附加的隐含形参,即指向该类对象的一个指针。这个隐含形参命名为this。

    类的成员方法一经编译,形参都会加上当前类类型的this指针变量。

    在对象调用成员方法====》把对象的地址作为方法的实参进行传递。

    在方法里面访问其他成员,都是通过this->来调用的。

9.浅拷贝和深拷贝

    浅拷贝:简单来说,浅拷贝可以理解为直接将值复制给将要拷贝的对象。若其中有变量指向外部资源,那么也只是会直接指向同一块外部资源。

    深拷贝:可以理解为,若一个类拥有外部资源,且当这个类的对象发生拷贝的时候,会为新对象分配新的资源。

    >预防浅拷贝问题的方法:

        1)自定义拷贝构造函数和赋值运算符重载函数

        2)将这两个函数私有化,从根本上杜绝浅拷贝的问题

class A
{
public:
    A(int a,int b,int c)
    :_x(a),_y(b),_z(c)//初始化列表
    {}
private:
    int _x;
    int & _y;
    const int _z;
};

 

10.拷贝构造函数

 

    场景:同类型一个已经存在的对象构造新对象的时候,就会调用拷贝构造函数。

    结论:当类中没有提供拷贝构造函数时,编译器就会产生一个默认的拷贝构造函数,做内存拷贝(浅拷贝)。

    带来的问题:若类中有成员指向外部资源的时候,若没有自定义拷贝构造函数,那么会做内存拷贝。当被拷贝构造的对象析构时,直接就释放了指向的外部资源,原对象再次析构就会出现问题,因为他所指向的外部资源已经被释放掉了,释放两次势必是会出错的。所以,当对象浅拷贝出错,一定要提供自定义的拷贝构造函数。

class A
{
public:
    A()
    {
        cout<<"A()"<<endl;
        a = 10;
        mp = new int[a];
    }
    A(const A &src)//没有任何操作 实行默认拷贝构造(浅拷贝)
    {
        cout<<"copy A"<<endl;
    }
    ~A()
    {
        cout<<"~A()"<<endl;
        delete []mp;
    }
private:
    int a;
    int *mp;
};
int main()
{
    A a;//定义一个A类的对象
    A b = a;//已经存在的对象b构造新对象a 
    return 0;
    
}

11.赋值运算符重载函数operator=

    场景:两个已经存在的对象赋值的时候,会调用该函数。

    结论:当类里面没有提供赋值运算符重载函数的时候,编译器就会产生默认的赋值运算符重载函数,作内存拷贝(浅拷贝)。所以同理,如果类中有成员变量指向外部成员的时候,浅拷贝会出错,就应该自定义赋值运算符重载函数。

class A
{
public:
    A()
    {
        cout<<"A()"<<endl;
        a = 10;
        mp = new int[a];
    }
    void operator=(const A &src)
    {
        cout<<"operator "<<endl;
    }
    ~A()
    {
        cout<<"~A()"<<endl;
        delete []mp;
    }
private:
    int a;
    int *mp;
};
int main()
{
    A a;
    A b;
    b = a;
    return 0;
}

12.拷贝构造函数的形参可不可以按值传递呢

    回答:不可以。如果类A的拷贝构在函数是以值方式传递的一个类A对象作为参数的话,当需要调用拷贝构造函数的时候,需要以值方式传递一个A类的对象作为实参,而以值方式传递需要调用类A的拷贝构造函数;结果就是调用类A的拷贝构造函数导致又调用了类A的拷贝构造函数,造成无限递归。

 13.对象的构造函数初始化列表

    如果想在对象成员变量初始化的时候,指定成员变量的初始化方式,那么应该在构造函数的初始化列表中指定。

class A
{
public:
    A(int a,int b,int c)
    :_x(a),_y(b),_z(c)//初始化列表
    {}
private:
    int _x;
    int & _y;//引用必须在定义的时候初始化,并且不能重新赋值,所以必须在初始化列表中初始化
    const int _z;//常量成员只能初始化不能赋值,所以必须在初始化列表中初始化
};

    需要注意:成员的初始化顺序是在类中定义出现的顺序初始化的,而不是按照初始化列表的顺序初始化的。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值