栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++类对象详解

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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 < 
构造函数和析构函数以及深拷贝和浅拷贝

构造函数和析构函数 : 
        构造函数: 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 
using namespace std;
class SeqStack
{
    public:
    //构造函数
        SeqStack(int size = 10)
        {
            cout << this << " SeqStack()" <push(70);
    ps->push(80);
    ps->pop();
    cout << ps->top() < 
深拷贝例子:
#include 
#include 
using namespace std;

class String
{
    public:
        String(const char *str = nullptr) //普通构造函数
        {   if (nullptr == str) 
            {
                m_data = new char[1];
                *m_data = '';
                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 
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 < 
 构造函数和初始化列表

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

#include 
#include 
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 <  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 <  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;
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/718190.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号