C++类相关的操作

C++类操作相关

类的构造函数

  • 构造函数与类名相同,没有返回值,参数列表可以传入多组参数,即构造函数不唯一,下面使用的是初始化成员列表,即可以在冒号后面,使用括号的方法对其进行初始化
  • 注意:在定义类的时候,使用=号时,并不是赋值(assignment),而是调用了构造(constructor)函数
  • MyClass001.h

    #ifndef MYCLASS001_H
    #define MYCLASS001_H
    
    #include <stdio.h>
    #include <cstdlib>
    #include <iostream>
    #include <string>
    #include <set>
    #include <string.h>
    #include <map>
    #include <unordered_map>
    #include <unordered_set>
    #include <iostream>
    #include <queue>
    #include <list>
    #include <assert.h>
    #include <time.h>
    #include <algorithm>
    #include <functional>
    #include <iterator>
    #include <fstream>
    using namespace std;
    
    class Myclass001
    {
    public:
        Myclass001(int a = 0, int b = 0, int c = 0)
            : _a(a), _b(b), _c(c)  // 相当于将a赋值给_a,以此类推
        {
            cout << "a : " << _a << ", b :" << _b << ", c : " << _c << endl;
        }
    
    private:
        int _a;
        int _b;
        int _c;
    };
    
    #endif
    
  • main.cpp

    #include <stdio.h>
    #include "MyClass001.h"
    using namespace std;
    typedef long long ll;
    
    int main()
    {
        // 按照时间来初始化种子,保证每次结果不一样
        srand( (int)time(0) );
        //cout << rand() << endl;
    
        // 4种初始化方式
        Myclass001 mc1; // 如果参数列表为空,则不用加括号,表示不带参数的构造函数
        Myclass001 mc2(2);
        Myclass001 mc3(2,3);
        Myclass001 mc4(2, 3, 4);
    
        // 这里即使是=的符号,也是构造函数,相当于传入一个参数进行构造
        Myclass001 mc5 = 5; 
    
        system("pause");
        return 0;
    }
    

析构函数

  • 在类中申请内存空间等资源时,需要在类的生命结束时,在析构函数中释放这些内存空间

  • MyClass02.h

    #ifndef MyCLASS002_H
    #define MyCLASS002_H
    
    #include <stdio.h>
    #include <cstdlib>
    #include <iostream>
    #include <string>
    #include <set>
    #include <string.h>
    #include <map>
    #include <unordered_map>
    #include <unordered_set>
    #include <iostream>
    #include <queue>
    #include <list>
    #include <assert.h>
    #include <time.h>
    #include <algorithm>
    #include <functional>
    #include <iterator>
    #include <fstream>
    using namespace std;
    
    class MyClass002
    {
    public:
        MyClass002(int row, int col)
            : _row(row), _col(col)
        {
            cout << "apply for memory" << endl;
            pmat = new double[ _row * _col ];
        }
    
        ~MyClass002()
        {
            cout << "free memory" << endl;
            delete []pmat;
        }
    
    private:
        int _row, _col;
        double *pmat;
    };
    
    #endif
    
  • main.cpp

    #include <stdio.h>
    #include "MyClass002.h"
    using namespace std;
    typedef long long ll;
    
    // 在函数中测试,可以看到调用析构函数时的状态
    void testClass()
    {
        MyClass002 mc(3, 4);
    }
    
    int main()
    {
        // 按照时间来初始化种子,保证每次结果不一样
        srand( (int)time(0) );
        //cout << rand() << endl;
    
        testClass();
    
        system("pause");
        return 0;
    }
    

类成员逐一初始化

  • 对于一般所谓情况,进行类之间直接赋值时B=A,会将A中的元素逐一复制到B中,这对于一些常用的简单类型数据是没有问题的,但是如果类中含有地址等变量的话,则这种复制会造成很大的问题:两个类会共享同一块内存空间,如果一个类被析构了,这一块内存空间被释放了,则在另外一个类中继续使用是很危险的事情,因此对于有些类,需要单独写一个copy的构造函数,
  • 下面首先给出一个错误的代码示例
  • Class002.h

    #ifndef MyClass002_H
    #define MyClass002_H
    
    #include <stdio.h>
    #include <cstdlib>
    #include <iostream>
    #include <string>
    #include <set>
    #include <string.h>
    #include <map>
    #include <unordered_map>
    #include <unordered_set>
    #include <iostream>
    #include <queue>
    #include <list>
    #include <assert.h>
    #include <time.h>
    #include <algorithm>
    #include <functional>
    #include <iterator>
    #include <fstream>
    using namespace std;
    
    class MyClass002
    {
    public:
        MyClass002(int row, int col)
            : _row(row), _col(col)
        {
            cout << "apply for memory" << endl;
            pmat = new double[ _row * _col ];
        }
    
        ~MyClass002()
        {
            cout << "free memory" << endl;
            delete[]pmat;
            pmat == NULL;
    
        }
    
        bool SetValue(int r, int c, int val)
        {
            if (r < 0 || r > _row || c < 0 || c > _col)
                return false;
            pmat[r*_col+c] = val;
            return true;
        }
    
        int getValue(int r, int c)
        {
            if (r < 0 || r > _row || c < 0 || c > _col)
                return  INT_MIN;
            return pmat[r*_col + c];
        }
    
    private:
        int _row, _col;
        double *pmat;
    };
    
    #endif
    
  • main.cpp

    #include <stdio.h>
    #include "MyClass002.h"
    using namespace std;
    typedef long long ll;
    
    // 这个程序无法结束,因为系统在已经释放掉的资源时出现问题,而且修改了mc1中的内容后,mc2中的变量也被修改,不符合设计要求
    void testClass()
    {
        MyClass002 mc1(3, 4);
    
        MyClass002 mc2 = mc1;
    
        mc1.SetValue( 0,0,10 );
        cout << mc2.getValue(0, 0) << endl;
    
    }
    
    int main()
    {
        // 按照时间来初始化种子,保证每次结果不一样
        srand( (int)time(0) );
        //cout << rand() << endl;
    
        testClass();
    
        system("pause");
        return 0;
    }
    
  • 在进行copy constructor时,可以在函数内访问类的private成员变量,下面给出正确的copy constructor写法

  • MyClass002.h

    #ifndef MYCLASS002_H
    #define MYCLASS002_H
    
    #include <stdio.h>
    #include <cstdlib>
    #include <iostream>
    #include <string>
    #include <set>
    #include <string.h>
    #include <map>
    #include <unordered_map>
    #include <unordered_set>
    #include <iostream>
    #include <queue>
    #include <list>
    #include <assert.h>
    #include <time.h>
    #include <algorithm>
    #include <functional>
    #include <iterator>
    #include <fstream>
    using namespace std;
    
    class MyClass002
    {
    public:
        MyClass002(int row, int col)
            : _row(row), _col(col)
        {
            cout << "apply for memory" << endl;
            pmat = new double[ _row * _col ];
        }
    
        // 在类中,该类的实例可以访问对应的private变量
        MyClass002(const MyClass002& mc)
            : _row(mc._row), _col(mc._col)
        {
            cout << "copy constyructor" << endl;
            int cnt = _row * _col;
            pmat = new double[cnt];
            for (int i = 0; i < cnt; i++)
                pmat[i] = mc.pmat[i];
        }
    
        ~MyClass002()
        {
            cout << "free memory" << endl;
            delete[]pmat;
            pmat == NULL;
    
        }
    
        bool SetValue(int r, int c, int val)
        {
            if (r < 0 || r > _row || c < 0 || c > _col)
                return false;
            pmat[r*_col+c] = val;
            return true;
        }
    
        int getValue(int r, int c)
        {
            if (r < 0 || r > _row || c < 0 || c > _col)
                return  INT_MIN;
            return pmat[r*_col + c];
        }
    
    private:
        int _row, _col;
        double *pmat;
    };
    
    #endif
    
  • main.cpp

    #include "MyClass002.h"
    using namespace std;
    typedef long long ll;
    
    void testClass()
    {
        MyClass002 mc1(3, 4);
        // 使用类的构造函数进行初始化,会重新申请内存空间
        MyClass002 mc2 = mc1;
    
        // 两块内存空间相互不干扰,程序可以正常运行
        mc2.SetValue(0,0,5 );
        mc1.SetValue( 0,0,10 );
        cout << mc2.getValue(0, 0) << endl;
    }
    
    int main()
    {
        // 按照时间来初始化种子,保证每次结果不一样
        srand( (int)time(0) );
        //cout << rand() << endl;
    
        testClass();
    
        system("pause");
        return 0;
    }
    

const修饰成员函数

  • 对于不可以修改的函数参数,我们可以使用const去进行限定,但是无同时也需要确保函数中的任意一个使用了该const变量的子函数也不对其进行修改,为了使得编译器知道在调用这些函数时不会对值进行修改,需要对成员函数使用const进行修饰,相当于告诉编译器:这个成员函数不会修改类对象的内容
  • 如果类作为返回值(引用等),同时函数被const修饰了,则也需要确保将返回值限定为const
  • MyClass003.h

    #ifndef MYCLASS003_H
    #define MYCLASS003_H
    
    #include <stdio.h>
    #include <cstdlib>
    #include <iostream>
    #include <string>
    #include <set>
    #include <string.h>
    #include <map>
    #include <unordered_map>
    #include <unordered_set>
    #include <iostream>
    #include <queue>
    #include <list>
    #include <assert.h>
    #include <time.h>
    #include <algorithm>
    #include <functional>
    #include <iterator>
    #include <fstream>
    using namespace std;
    
    class MyClass003
    {
    public:
        MyClass003(int a = 0, int b = 0, int c = 0)
            : _a(a), _b(b), _c(c)  // 相当于将a赋值给_a,以此类推
        {
            //cout << "a : " << _a << ", b :" << _b << ", c : " << _c << endl;
        }
    
        // 
        int sumA(const MyClass003 &mc)
        {
            return mc.getA() + _a;
        }
    
        // 如果不加这个const,在上面调用时,会出错,因为编译器无法判断getA是否修改了类
        int getA() const
        {
            return _a;
        }
    
        // 不可以修改的引用,在这里删除前面的const会报错,因为后面的const说明不对这个类进行修改
        // 前面也需要设置为const来确保这一操作
        const int &getARef() const
        {
            return _a;
        }
    
        // 可以修改的引用
        // 重载上面的函数
        int &getARef()
        {
            return _a;
        }
    
        // 因为b被标记为mutable,因此修改其值并不会破坏整个class obj的完整性
        int &testMutable() const
        {
            _b++;
        }
    
    private:
        int _a;
        mutable int _b;
        int _c;
    };
    
    #endif
    
  • main.cpp

    #include "MyClass003.h"
    using namespace std;
    typedef long long ll;
    
    void testClass()
    {
        MyClass003 mc1(1,2,3);
        MyClass003 mc2(2,3,4);
    
        cout << mc1.sumA(mc2) << endl;
    
        int &a = mc1.getARef(); //因为a是mc1中_a的引用,因此修改a可以直接修改mc1._a
        const int &b = mc1.getARef();
        a = 2;
        cout << mc1.getARef() << endl;
    
    }
    
    int main()
    {
        // 按照时间来初始化种子,保证每次结果不一样
        srand( (int)time(0) );
        //cout << rand() << endl;
    
        testClass();
    
        system("pause");
        return 0;
    }
    

this与static成员变量(函数)

  • static可以用于修饰那些与其他类成员变量或者函数无关的变量,在定义static成员函数时,其中不能使用non-static成员函数,但是它可以被其他non-static成员函数使用
  • 注意:static成员变量与函数存在于整个应用程序的生命周期
  • MyClass004.h

    #ifndef MYCLASS004_H
    #define MYCLASS004_H
    
    #include <stdio.h>
    #include <cstdlib>
    #include <iostream>
    #include <string>
    #include <set>
    #include <string.h>
    #include <map>
    #include <unordered_map>
    #include <unordered_set>
    #include <iostream>
    #include <queue>
    #include <list>
    #include <assert.h>
    #include <time.h>
    #include <algorithm>
    #include <functional>
    #include <iterator>
    #include <fstream>
    using namespace std;
    
    class MyClass004
    {
    public:
        MyClass004(int a = 0, int b = 0, int c = 0)
            : _a(a), _b(b), _c(c)
        {
        }
    
        // this的使用,是指向当前class obj的指针
        MyClass004 copy( const MyClass004& mc )
        {
            if (this != &mc)
            {
                this->_a = mc._a;
                this->_b = mc._b;
                this->_c = mc._c;
            }
            return *this;
        }
        static int Add(const int& a, const int& b)
        {
            return a + b;
            // _a = 1; //这句话有问题,因为静态函数中不能使用非静态的函数或者成员变量
        }
    
        void printInfo()
        {
            cout << "a : " << _a << ", b :" << _b << ", c : " << _c << endl;
        }
    
    private:
        int _a;
        int _b;
        int _c;
    };
    
    #endif
    
  • main.cpp

    #include "MyClass004.h"
    using namespace std;
    typedef long long ll;
    
    void testClass()
    {
        MyClass004 mc1(1,2,3);
        MyClass004 mc2(4,5,6);
        mc2.printInfo();
        MyClass004 mc3 = mc2.copy( mc1 );
        mc2.printInfo();
        mc3.printInfo();
        cout << MyClass004::Add( 1,2 ) << endl;
    }
    
    int main()
    {
        // 按照时间来初始化种子,保证每次结果不一样
        srand( (int)time(0) );
        //cout << rand() << endl;
    
        testClass();
    
        system("pause");
        return 0;
    }
    

运算符重载与友元

  • 关于运算符重载

    • 不能引入新的运算符,除.,*,::,?:之外,其他的运算符都可以被重载
    • 运算符的操作数个数不可变,比如==的操作符是2个,则重载时也必须是2个。
    • 运算符的优先级不可改变
    • 运算符的参数列表中,必须至少有一个参数类型为class类型,即不能为non-class类型定义其已经存在的运算符。
    • 对于++运算符重载,为了避免冲突,在后缀自增时,会添加一个参数,一般为int(但是不会使用这个int)
  • 关于友元

    • 可以指定某个函数是一个类的友元,这个函数可以不属于任何类,也可以指定类A是类B的友元,这样就可以在类A中使用类B的private变量与函数
  • MyClass005.h

    #ifndef MYCLASS005_H
    #define MYCLASS005_H
    
    #include <cstdlib>
    #include <string>
    #include <iostream>
    #include <assert.h>
    #include <time.h>
    #include <algorithm>
    #include <functional>
    #include <iterator>
    #include <fstream>
    using namespace std;
    
    // 需要首先声明这个class的存在
    class FriendClass005;
    class MyClass005
    {
        // 指定FriendClass005为当前class的友元
        friend FriendClass005; // 声明FriendClass005是MyClass005的友元
    public:
        MyClass005(int a = 0)
            : _a(a)
        {
        }
    
        bool operator==(const MyClass005& mc) const
        {
            return mc._a == this->_a;
        }
    
        bool operator!=(const MyClass005& mc) const
        {
            return !(mc == *this);
        }
    
        // 相当于++class,前缀自增
        MyClass005 operator++()
        {
            ++_a;
            MyClass005 tmp = *this;
            return *this;
        }
    
        // 加入1个参数,防止和前面的冲突,后缀自增
        MyClass005 operator++(int)
        {
            MyClass005 tmp = *this;
            ++_a;
            return tmp;
        }
    
        void printInfo()
        {
            cout << "a : " << _a << endl;
        }
    private:
        int _a;
    };
    
    
    class FriendClass005
    {
    public:
        // 这个函数是这个类的友元,因此可以在下面定义时,直接使用其内部private变量
        friend void printClass005Info(const FriendClass005& fc);
    
        // 在MyClass005的友元中使用其private变量
        void disp(const MyClass005& mc)
        {
            cout << mc._a << endl;
        }
    private:
        int _num;
    };
    
    void printClass005Info(const FriendClass005& fc)
    {
        cout << "disp a in mc's friend class : " << fc._num << endl;
    }
    
    #endif
    
  • main.cpp

    #include "MyClass005.h"
    using namespace std;
    typedef long long ll;
    
    void testClass()
    {
        MyClass005 mc1(2);
        mc1.printInfo();
        auto mc2 = mc1++;
        mc1.printInfo();
        mc2.printInfo();
        FriendClass005 fc;
        fc.disp(mc2);
    
        cout << "===auto increase test===" << endl;
        auto mc3 = mc1++;
        auto mc4 = ++mc1;
        mc3.printInfo();
        mc4.printInfo();
    
    }
    
    int main()
    {
        // 按照时间来初始化种子,保证每次结果不一样
        srand( (int)time(0) );
        //cout << rand() << endl;
    
        testClass();
    
        system("pause");
        return 0;
    }
    

function object

  • function object是指提供function call运算符的class,用来解决诸如lt(val)这样的函数调用,lt可以是函数指针,也可以是class boject,当它是class object时,会将这个操作转化为lt.operator(val)function call可以接受任意个数的参数

  • MyClass006.h

    #ifndef MYCLASS006_H
    #define MYCLASS006_H
    
    #include <cstdlib>
    #include <string>
    #include <iostream>
    #include <assert.h>
    #include <time.h>
    #include <algorithm>
    #include <functional>
    #include <iterator>
    #include <fstream>
    #include <vector>
    using namespace std;
    
    class LessThan
    {
    public:
        LessThan(int val) : _val(val)
        {
            nums = {1,2,3,4,5,6};
        }
    
        // read base value
        int compVal() const
        {
            return _val;
        }
    
        // write base value
        int compVal(int nval)
        {
            _val = nval;
        }
    
        // 实现() 的function object
        bool operator() (int val) const
        {
            return val < _val;
        }
    
        // 实现[]的function object
        int operator[] (int index) const
        {
            if (index < 0 || index >= nums.size())
                return -1;
            return nums[index];
        }
    
    private:
        int _val;
    
        vector<int> nums;
    };
    
    #endif
    
  • main.cpp

    #include "MyClass006.h"
    using namespace std;
    typedef long long ll;
    
    int countLessThan(const vector<int>& nums, int val)
    {
        int cnt = 0;
        LessThan lt(val);
        for (auto num : nums)
            if (lt(num))
                cnt++;
    
        return cnt;
    }
    
    void printLessThan(const vector<int>& nums, int val, ostream& os = cout)
    {
        int cnt = 0;
        LessThan lt(val);
        for (auto num : nums)
            if (lt(num))
                cout << num << " ";
        cout << endl;
    }
    
    void testClass()
    {
        vector<int> nums = {1,10,5,4,3,6,7,2,8,9};
        cout << countLessThan(nums, 5) << endl;
        printLessThan( nums, 5 );
        LessThan lt(0);
        cout << lt[3] << endl;
    }
    
    int main()
    {
        // 按照时间来初始化种子,保证每次结果不一样
        srand( (int)time(0) );
        //cout << rand() << endl;
    
        testClass();
    
        system("pause");
        return 0;
    }
    

重载iostream运算符

  • 输入输出重载在结束时都需要返回对应的iostream
  • 输入时,尽量不要将第二个class的参数设置为const,因为一般情况下我们都是需要对其进行修改的
  • 输入与输出重载都不是成员函数,因此需要设置为友元函数,否则会出错

  • MyClass007.cpp

    #ifndef MYCLASS007_H
    #define MYCLASS007_H
    
    #include <cstdlib>
    #include <string>
    #include <iostream>
    #include <ostream>
    #include <assert.h>
    #include <time.h>
    #include <algorithm>
    #include <functional>
    #include <iterator>
    #include <fstream>
    #include <vector>
    using namespace std;
    
    class MyClass007
    {
    public:
        MyClass007(int a = 0, int b = 0)
            :_a(a), _b(b)
        {
        }
    
        ~MyClass007()
        {
        }
    
        // 输入与输出的重载必须写成friend函数,否则编译出错,因为这个不是成员函数
        friend ostream& operator<< (ostream& os, const MyClass007& mc)
        {
            os << "MyClass007, a :" << mc._a << ", b : " << mc._b << endl;
            return os;
        }
    
        // 定义输入运算符的时候,不能将mc设置为const,否则无法通过编译(如果在其中对其修改的话)
        friend istream& operator>> (istream& is, MyClass007& mc)
        {
            is >> mc._a >> mc._b;
            return is;
        }
    
    private:
        int _a;
        int _b;
    };
    #endif
    
  • main.cpp

    #include "MyClass007.h"
    using namespace std;
    typedef long long ll;
    
    void testClass()
    {
        MyClass007 mc1(2);
        cin >> mc1;
        cout << mc1 << endl;
    }
    
    int main()
    {
        // 按照时间来初始化种子,保证每次结果不一样
        srand( (int)time(0) );
        //cout << rand() << endl;
    
        testClass();
    
        system("pause");
        return 0;
    }
    

指向类成员函数(class member function)的指针

  • 关于类成员函数的声明定义以及与普通的函数指针的区别:http://www.cnblogs.com/tracylee/archive/2012/11/15/2772176.html
  • 需要使用A.*b或者this->.*b去访问类成员函数
  • 声明成员函数列表时,需要加上static,否则会出现https://blog.csdn.net/cffishappy/article/details/6821222所示的问题

  • MyClass008.h

    #ifndef MYCLASS008_H
    #define MYCLASS008_H
    
    #include <cstdlib>
    #include <string>
    #include <iostream>
    #include <ostream>
    #include <assert.h>
    #include <time.h>
    #include <algorithm>
    #include <functional>
    #include <iterator>
    #include <fstream>
    #include <vector>
    using namespace std;
    
    class MyClass008
    {
    public:
        MyClass008()
        {
            result.resize(3);
            _pmf = &MyClass008::square;
        }
    
        void square(int a)
        {
            result[0] = a*a;
        }
        void linearTwo(int a)
        {
            result[1] = a*2;
        }
        void minus(int a)
        {
            result[2] = -a;
        }
    
        void setType(int idx)
        {
            _pmf = func_tbs[idx];
        }
    
        void setElem(int x)
        {
            (this->*_pmf)( x );
        }
    
        void dispInfo()
        {
            for (int i = 0; i < result.size(); i++)
                cout << result[i] << " ";
            cout << endl;
        }
    
        // 使用Typedef简化类型写法
        typedef void(MyClass008::*PtrType)(int);
    
        // 声明:需要使用static修饰,否则会出现
        // https://blog.csdn.net/cffishappy/article/details/6821222
        // 中所示的问题
        static PtrType func_tbs[3];
    
    private:
        vector<int> result;
        PtrType _pmf;
    };
    
    // 
    MyClass008::PtrType MyClass008::func_tbs[3] = { &MyClass008::square, &MyClass008::linearTwo, &MyClass008::minus };
    
    #endif
    
  • main.cpp

    #include "MyClass008.h"
    using namespace std;
    typedef long long ll;
    
    void testClass()
    {
        MyClass008 mc1;
        for (int i = 0; i < 3; i++)
        {
            mc1.setType(i);
            mc1.setElem( 4 );
            mc1.dispInfo();
        }
    }
    
    int main()
    {
        // 按照时间来初始化种子,保证每次结果不一样
        srand( (int)time(0) );
        //cout << rand() << endl;
    
        testClass();
    
        system("pause");
        return 0;
    }
    

猜你喜欢

转载自blog.csdn.net/u012526003/article/details/80072957