C++类对象详解

类和对象、this指针

OOP语言的四大特征是什么?:
    抽象     封装/隐藏     继承     多态

访问限定符:
            public 公有的          给外部提供公有方法,来访问私有的属性
            private  私有的       不能在类外访问  属性都是私有的
            protected 保护的

1.类体内实现的方法,自动处理成inline内联函数

2.常量字符串是不允许让普通指针接收

3.类的内存大小计算:
        只和成员变量有关,和成员方法无关
                char _name[20];  占1字节
                double _price;   占8字节
                int _amount;     占4字节
        首先找最长的占用字节类型,最长的是8字节,按照最长的补位:

                char   类型有20个就是20字节,能整除最长的字节也就是24;所以要补4个字节对齐
                double 类型有8字节
                int   类型也补了4空白字节对齐
        所以CGoods类的大小就是40字节

4.可以定义无数个对象,每个对象都有自己的成员变量,但是它们共享一套成员方法
        那么问题来了:
                show() => 怎么知道处理哪个对象的信息的呢?
                init(name, price, amount) =>怎么知道把信息初始化给哪一个对象的呢?
        this 指针就是做这个事的:
                类的成员方法一经编译,所有的方法参数,都会加一个this指针 比如:
                        init(&CGoods *this,const char * name,double price, int amount)
        所以调用init的时候 比如::
                goods.init(&goods,"面包",10.0,200); //这会把对象地址传了进去

class CGoods{
    public:
        //可以用一个函数初始化私有变量.
        void init(const char *name,double price, int amount);
        
        //打印
        void show();

        const char* getName()
        {
            return _name;
        }
        double getPrice()
        {
            return _price;
        }
        int getAmount()
        {
            return _amount;
        }

    private: 
        char _name[20];
        double _price;
        int _amount; 
};

//类外定义函数
void CGoods::init(const char *name, double price, int amount)
{
            strcpy(_name, name);
            _price = price;
            _amount = amount;
}

void CGoods::show()
{
    cout << "name:" << _name << ", _price:" << _price << ", amount:" << _amount <<endl; 
}

int main()
{


    CGoods goods; //初始化对象,
    goods.init("面包", 10.2, 100);
    goods.show();

    return 0;
}

构造函数和析构函数以及深拷贝和浅拷贝

构造函数和析构函数 : 
        构造函数: SeqStack()
                定义对象时,自动调用的;可以重载的,构造完成,对象就产生了
        析构函数: ~SeqStack()
                不带参数,不能重载,只有一个析构函数;出了当前作用域,就会自动调用析构函数,析构完成,这个对象就不存在了
    注意:没有提供任何构造函数的时候,会为你生成默认构造和默认析构,是空函数

深拷贝和浅拷贝 
        SeqStack s1(10);
        SeqStack s2 = s1; //拷贝
        浅拷贝 : 直接拷贝过去,是堆内存的数据也是直接拷贝内存的地址,所以s1,s2 指向的是同一块内存
        代码:

                SeqStack(const SeqStack &src)
                {
                        _pstack = src._pstack;
                        _price = src.price;
                        _amount = src._amount;
                }

        解释:
        s1
                int *_pstack;   ----> new 0x61feb4
                int _top ;      ----> 10
                int _size;      ----> 50
        s2
                int *_pstack;   ----> new 0x61feb4
                int _top ;      ----> 10
                int _size;      ----> 50
        1.析构的时候先是析构的s2,然后s2的_pstack被delete,s1的_pstack还是指向0x61feb4,所以到s1析构的时候,0x61feb4地址成野指针,释放野指针就会程序出现异常报错
        2.对象默认的拷贝构造是做内存的数据拷贝
        3.关键是对象如果占用外部资源,那么浅拷贝就出现问题了!
   

深拷贝:

        SeqStack(const SeqStack &src)
        {
                //防止自赋值
                if (this == &src) return ;

                _pstack = new int[src._size];
                for(int i = 0; i <= src._top; ++i)
                {
                        _pstack[i] = src._pstack[i];
                }
                _top = src._top;
                _size = src._size;

        }

seqStack.cpp

//OOP 实现顺序栈
#include <iostream>
using namespace std;
class SeqStack
{
    public:
    //构造函数
        SeqStack(int size = 10)
        {
            cout << this << " SeqStack()" <<endl;
            _pstack = new int[size];
            _top    = -1;
            _size   = size;
        }
    //析构函数
        ~SeqStack()
        {
            cout << this << " ~SeqStack()" <<endl;
            delete []_pstack;
            _pstack = nullptr;
        }
    //深拷贝
        SeqStack(const SeqStack &src)
        {
            _pstack = new int[src._size];
            for(int i = 0; i <= src._top; ++i)
            {
                _pstack[i] = src._pstack[i];
            }
            _top = src._top;
            _size = src._size;
        }
    //赋值重载函数
        void operator=(const SeqStack &src)
        {
            delete _pstack;
            _pstack = new int[src._size];
            for(int i = 0; i <= src._top; ++i)
            {
                _pstack[i] = src._pstack[i];
            }
            _top = src._top;
            _size = src._size;

        }
        //成员函数
        void push(int val)
        {
            if(full())
                resize();
            _pstack[++_top] = val;
        }
        void pop()
        {
            if(empty()) return ;
            --_top;
        }
        int top()
        {
            return _pstack[_top];
        }
        bool empty() {return _top == -1;}
        bool full() { return _top == _size -1; }

    private:
    // 成员变量
        int *_pstack; //动态开开辟数组,存储顺序栈的元素
        int _top ;    //指向栈顶元素的位置
        int _size;    //总容器大小

    private:
        void resize()
        {
            int *ptmp = new int[_size * 2];
            for(int i = 0; i < _size; ++i)
            {
                ptmp[i] = _pstack[i];
            }
            delete []_pstack;
            _pstack = ptmp;
            _size *= 2;
        }

};


int main()
{
    //开辟内存,调用构造
    SeqStack s(5);
    //栈上调用成员的方法
    s.push(1); 
   



    //堆上开辟内存必须要自己释放
    SeqStack *ps = new SeqStack(60);
    ps->push(70);
    ps->push(80);
    ps->pop();
    cout << ps->top() <<endl;
    delete ps; //这里的delete先调用类的析构函数,然后再把内存释放


    SeqStack s1(10);
    SeqStack s2 = s1; //拷贝构造函数
    SeqStack s3(s1);

    s2 = s1; //赋值操作,默认的赋值操作也是做浅拷贝

}

深拷贝例子:

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

class String
{
    public:
        String(const char *str = nullptr) //普通构造函数
        {   if (nullptr == str) 
            {
                m_data = new char[1];
                *m_data = '\0';
                return ;
            }
            m_data = new char[strlen(str) + 1];
            strcpy(m_data,str);
        }
        String(const String &thor)    //拷贝构造函数
        {
            if(this == &thor) return;
            m_data = new char[strlen(thor.m_data) + 1];
            strcpy(m_data,thor.m_data);
        }
        ~String(){
            delete []m_data;
            m_data = nullptr;
        }
        //赋值重载函数      String& 是为了支持连续的operator= 赋值操作
        String& operator=(const String &thor){
            if(this != &thor) 
            {
                delete []m_data;
                m_data = new char[strlen(thor.m_data) + 1];
                strcpy(m_data,thor.m_data);
            }
            return *this;
        }
    private:
        char *m_data;//用于保存字符串
};

int main(){
    String s;
    String s1("Hello");
    String s2 = s1;
    String s3 = s2;
    s2 = s1;
    s3 = s2 = s1; //连续赋值

    return 0;
}
#include <iostream>
using namespace std;

class Queue
{
    public:
        Queue(int size = 10){
            _pQue = new int[size];
            _front = _rear = 0;
            _size = size;
        }
        //禁用拷贝构造和赋值构造
        // Queue(const Queue &src) = delete;
        // Queue& operator=(const Queue &src) = delete;

        Queue(const Queue &src) {
            if(this == &src) return ;
            _size = src._size;
            _front = src._front;
            _rear = src._rear;
            _pQue = new int[_size];
            for (int i = _front; i != _rear; i = (_rear + 1) % _size )
            {
                _pQue[i] = src._pQue[i];
            }
        }
        Queue& operator=(const Queue &src )
        {
            if(this == &src) return *this;
            delete []_pQue;
            _size = src._size;
            _front = src._front;
            _rear = src._rear;
            _pQue = new int[_size];
            for(int i = _front; i != _rear; i = (_rear + 1) % _size)
            {
                _pQue[i] = src._pQue[i];
            }
            return *this;
        }
        ~Queue(){
            delete []_pQue;
            _pQue = nullptr;
        }
        bool full()
        {
            return (_rear + 1) % _size == _front;
        }
        bool empty()
        {
            return _front == _rear;
        }
        
        void push(int val)
        {
            // cout << val <<endl;
            if (full()) resize();

            _pQue[_rear] = val;
            _rear = (_rear + 1) % _size;
        }
        void pop()
        {
            if(!empty()) _front = (_front + 1) % _size;
        }
        int front()
        {
            return _pQue[_front];
        }
    private:
        int *_pQue; // 申请队列的数组空间
        int _front; //指向队头的位置
        int _rear;  //指向队尾的位置
        int _size;  //队列扩容的总大小

        void resize()
        {
            int *temp = new int[_size * 2];
            int index = 0;
            
            for (int i = _front; i != _rear; i = ( i + 1) % _size)
            {
                temp[index ++] = _pQue[i];
            }
            delete []_pQue;
            _pQue = temp;
            _front = 0;
            _rear = index;
            _size *= 2;
        }
};

int main()
{
    Queue queue;
    for(int i = 0; i < 20; ++i)
    {
        queue.push(rand() % 100);
    }
    while (!queue.empty())
    {
        cout << queue.front() << "\t";
        queue.pop();
    }
    return 0;
}

 构造函数和初始化列表

构造函数的初始化列表 
    1.初始化列表给成员变量是直接初始化的,比如 int a = 1;
    2.构造的函数体内初始化变量是先定义,然后再赋值;
    3.特殊变量必须要用初始化列表,比如:
        一个类中包含成员对象,成员对象是自定义的构造函数:CDate _date;
            1)如果在函数体内初始化就会发生这样的事情:先定义 CDate _date;就会调用默认构造,我们自定义的构造函数,所以找不到默认构造就会出错,就会编译错误
            2)如果在初始化列表中就会直接初始化:CDate _date = CDate(y,m,d)

    4.成员变量的初始化和它们定义的顺序有关,和构造函数初始化列表中的出现的先后顺序无关!

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

//构造函数的初始化列表

class CDate
{
    public:
        CDate(int y, int m, int d) //自定义构造函数,编译器就不会产生默认构造了
        {
            _year = y;
            _month = m;
            _day = d;
        }
        void show()
        {
            cout << _year <<"-" << _month <<"-" << _day <<endl;
        }
    private:
        int _year;
        int _month;
        int _day;
};



class CGoods
{
    public:                                            
        CGoods(const char *n,int a,double p,int y,int m,int d)
        :_date(y,m,d)  //#1 构造函数的初始化列表 ==>  CDate(y,m,d)
        ,_amount(a) //相当于 int _amount = a;  直接初始化
        {
            //#2 当前类类型构造函数体
            strcpy(_name,n);
            //相当于 int _price, _price = p;   先定义后赋值
            _price = p;
            
        }
        void show()
        {
            cout << "name:" << _name << endl;
            cout << "amount:" << _amount << endl;
            cout << "price:" << _price << endl;
            _date.show();
        }
    private:
        char _name[20];
        int _amount;
        double _price;
        CDate _date;   //成员对象 1.分配内存 2.调用构造函数
};



int main()
{
    CGoods goods1("面包1",100,15.0,2022,1,21);
    goods1.show();

    CGoods goods2("面包2",200,25.0,2022,1,21);
    goods2.show();

    CGoods goods3("面包3",300,35.0,2022,1,21);
    goods3.show();

    CGoods goods4("面包4",400,45.0,2022,1,21);
    goods4.show();

    CGoods goods5("面包5",500,55.0,2022,1,21);
    goods5.show();

    return 0;
}

 类的各种成员方法以及区别

假设上面的CGoods类商品需要统计一下它的数量,该怎么统计呢?

        1.如果在类里边直接定义一个_count() 就会变成每个类都有一个了,所以这个行不通

        2.虽然全局变量可以,但是我们现在需要用类成员来统计,所以也不行

        3.所以这个应该使用静态的成员变量

                静态成员变量:在类内部定义,必须要在类外进行定义并且初始化

                                         不属于对象,而是属于类级别的全局变量

        4.静态方法没有this指针,不需要接受对象的地址,调用的时候用类名的作用域来调用就行

        注意:静态方法可以任意访问对象的私有成员,仅限于不依赖对象的成员(只能调用其他的static静态成员)

普通变量和静态成员变量的核心区别就是:

        普通的方法 ===》编译器会添加一个this行参变量

        static静态成员方法===〉 不会生成this行参

总结:

普通成员方法:

        1.属于类的作用域

        2.调用该方法时,需要依赖一个对象(常对象是无法调用的,实参const CGoods*   CGoods *this)

        3.可以任意访问对象的私有方法 protected继承。public private

static静态成员方法:

        1.属于类的作用域

        2.用类名作用域来调用方法

        3.可以任意访问对象的私有成员,仅限于不依赖对象的成员(只能调用其他static静态成员)

const常方法:

        1.属于类的作用域

        2.调用依赖一个对象,普通对象或者常对象都可以

        3.可以任意访问对象的私有成员,但是只能读不能写。

class CDate
{
    public:
        CDate(int y, int m, int d) //自定义构造函数,编译器就不会产生默认构造了
        {
            _year = y;
            _month = m;
            _day = d;
        }
        void show()
        {
            cout << _year <<"-" << _month <<"-" << _day <<endl;
        }
        void show() const
        {
            cout << _year <<"-" << _month <<"-" << _day <<endl;
        }
    private:
        int _year;
        int _month;
        int _day;
};



class CGoods
{
    public:                                            
        CGoods(const char *n,int a,double p,int y,int m,int d)
        :_date(y,m,d)  //#1 构造函数的初始化列表 ==>  CDate(y,m,d)
        ,_amount(a) //相当于 int _amount = a;  直接初始化
        {
            //#2 当前类类型构造函数体
            strcpy(_name,n);
            //相当于 int _price, _price = p;   先定义后赋值
            _price = p;
            _count ++;//构造的时候增加一个商品统计
            
        }
        void show()
        {
            cout << "name:" << _name << endl;
            cout << "amount:" << _amount << endl;
            cout << "price:" << _price << endl;
            _date.show();
        }
        //常成员方法 const CGoos *this
        void show() const
        {
            cout << "name:" << _name << endl;
            cout << "amount:" << _amount << endl;
            cout << "price:" << _price << endl;
            _date.show();
        }
        //静态成员方法,只需要用类名调用
        static void showCGoodsCount()
        {
            cout << "所有商品的数量:" << _count << endl;
        }
    private:
        char _name[20];
        int _amount;
        double _price;
        CDate _date;   //成员对象 1.分配内存 2.调用构造函数

        //静态成员变量,在这里只是一个声明,必须要要在类外去定义,并且初始化
        //存在于数据段上,只有一个
        static int _count; 
};

//定义静态变量
int CGoods::_count = 0; 


int main()
{
    CGoods goods1("面包1",100,15.0,2022,1,21);
    goods1.show();

    CGoods goods2("面包2",200,25.0,2022,1,21);
    goods2.show();

    CGoods goods3("面包3",300,35.0,2022,1,21);
    goods3.show();

    CGoods goods4("面包4",400,45.0,2022,1,21);
    goods4.show();

    CGoods goods5("面包5",500,55.0,2022,1,21);
    goods5.show();

    //只能调用常方法
    //只要是只读操作的成员方法,一律实现成const常成员方法
    const CGoods goods6("样品面包",500,55.0,2022,1,21);
    goods6.show();

    CGoods::showCGoodsCount();
    return 0;
}

指向类成员的指针

class Test
{
    public:
        void func() 
        {
            cout << "call Test::func" << endl;
        }

        static void static_func()
        {
            cout << "Test::static_func" << endl;
        }
        int ma;
};

指向成员变量的指针:

int main()
{
    Test t1;
    Test *t2 = new Test();
    int Test::*p = &Test::ma;
    t1.*p = 20;
    cout << t1.*p << endl;
    
    t2->*p = 30;
    cout << t2->*p << endl;
    return 0;
}

指向成员方法的指针:

int main()
{
    Test t1;
    Test *t2 = new Test();
    
    void (Test::*pfunc)() = &Test::func;
    (t1.*pfunc)();
    (t2->*pfunc)();
    delete t2;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

CID( ͡ _ ͡°)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值