溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++語言中類與對象是什么

發布時間:2021-11-24 13:39:02 來源:億速云 閱讀:189 作者:小新 欄目:編程語言

這篇文章給大家分享的是有關C++語言中類與對象是什么的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

一、構造函數(constructor)

1、構造函數簡介

C++語言中,構造函數是與類名相同的特殊成員函數。
在類對象創建時,自動調用構造函數,完成類對象的初始化。類對象本身是變量,在棧、堆上創建的對象,對象的成員初始化為隨機值;在靜態存儲區創建的對象,對象的成員初始化為0。

2、構造函數的定義

構造函數聲明的語法如下:
classname(parameters);
沒有參數的構造函數稱為無參構造函數。當類中沒有定義構造函數(包括拷貝構造函數)時,編譯器默認提供一個無參構造函數,并且其函數體為空。如果類中已經定義了構造函數(包括拷貝構造函數),編譯器不會再提供默認的無參構造函數。

#include <iostream>

using namespace std;

class Person
{
public:
    const char* name;
    int age;
    void print()
    {
        printf("My name is %s, I'm is %d years old.\n",name,age);
    }
};

int main(int argc, char *argv[])
{
    Person p;//編譯器提供默認的無參構造函數
    p.name = "Bauer";
    p.age = 30;
    Person p1 = p;//編譯器提供默認的拷貝構造函數
    p1.print();
    return 0;
}

上述代碼中,編譯器提供了默認的無參構造函數和拷貝構造函數。

#include <iostream>

using namespace std;

class Person
{
public:
    const char* name;
    int age;
    Person(const Person& another)
    {
        name = another.name;
        age = another.age;
    }
    void print()
    {
        printf("My name is %s, I'm is %d years old.\n",name,age);
    }
};

int main(int argc, char *argv[])
{
    //error: no matching function for call to 'Person::Person()'
    Person p;//編譯器不在提供默認的無參構造函數
    p.name = "Bauer";
    p.age = 30;
    Person p1 = p;
    p1.print();
    return 0;
}

上述代碼中,類中定義了一個拷貝構造函數,編譯器不會再提供默認的無參構造函數,創建Person對象時找不到構造函數調用,編譯器報錯。

3、構造函數的規則

構造函數的規則如下:
A、在對象創建時自動調用,完成初始化相關工作。
B、無返回值,與類名相同,默認無參,可以重載,可使用默認參數。
C、一旦顯示實現構造函數,C++編譯器不再為類實現默認構造函數。
構造函數的調用根據重載函數的匹配規則進行調用。

4、構造函數的參數初始化

classname::classname():member1(v1),member2(v2),...
{
    //code 
}

構造函數的初始化列表中成員的初始化順序與類中成員的聲明順序相同,與成員在初始化列表中的位置無關,初始化列表先于構造函數的函數體執行。

#include <iostream>

using namespace std;

class Value
{
private:
    int i;
public:
    Value(int value)
    {
        i = value;
        cout << i << endl;
    }
};

class Test
{
private:
    Value value2;
    Value value3;
    Value value1;
public:
    Test():value1(1),value2(2),value3(3)
    {}
};

int main(int argc, char *argv[])
{
    Test test;
    //2
    //3
    //1
    return 0;
}

類中定義的const變量的初始化必須在初始化列表中進行,本質是只讀變量。
編譯器無法直接得到類的const成員的初始值,因此const成員無法進行符號表。

#include <iostream>

using namespace std;

class Test
{
private:
    const int i;
public:
    Test():i(10)
    {}
    int getI()const
    {
        return i;
    }
    void setI(const int value)
    {
        int* p = const_cast<int*>(&i);
        *p = value;
    }
};

int main(int argc, char *argv[])
{
    Test test;
    cout << test.getI() << endl;//10
    test.setI(100);
    cout << test.getI() << endl;//100
    return 0;
}

5、類對象的構造順序

類對象根據存儲區域分為三種,其構造順序如下:
A、局部對象的構造順序
當程序執行流到達對象的定義語句時進行構造,但是goto語句可能會跳過類對象的構造。
B、堆對象的構造順序
當程序執行流到達new語句時創建對象,使用new創建對象將自動觸發構造函數的調用。
C、全局類對象的構造順序
全局類對象的構造順序是不確定的,不同的編譯器使用不同的規則確定構造順序。全局類對象的構造是不確定的,因此盡量不使用有依賴關系的全局對象。

6、臨時對象

直接調用構造函數將會產生一個臨時對象,臨時對象的生命周期只有一條語句的時間,臨時對象的作用域只在一條語句中。
現代C++編譯器在不影響最終結果的前提下,會盡力減少臨時對象的產生。實際工程開發中應盡力減少臨時對象。
A a = A(10);
上述代碼實際沒有產生臨時對象,也沒有調用拷貝構造函數,C++編譯器實際優化為A a=10;

#include <iostream>

using namespace std;

class Test
{
private:
    int i;
public:
    Test(int i)
    {
        cout << "Test(int i): " << i <<endl;
    }
    Test(const Test& another)
    {
        i = another.i;
        cout << "Test(const Test& another)" << i<< endl;
    }
    Test create(int i)
    {
        return Test(i);
    }
};

Test create(int i)
{
    return Test(i);
}

int main(int argc, char *argv[])
{
    Test test1 = Test(10);//輸出:Test(int i): 10
    //等價于Test test1 = 10;
    Test test2 = create(100);//輸出:Test(int i): 100
    //等價于Test test1 = 100;
    return 0;
}

7、構造函數的調用順序

單個對象創建時,構造函數的調用順序如下:
A、調用父類的構造過程
B、調用成員變量的構造函數(調用順序與聲明順序相同)
C、調用類自身的構造函數
析構函數的調用順序與構造函數相反。

#include <iostream>

using namespace std;

class Member
{
    const char* ms;
public:
    Member(const char* s)
    {
        printf("Member(const char* s): %s\n", s);

        ms = s;
    }
    ~Member()
    {
        printf("~Member(): %s\n", ms);
    }
};

class Test
{
    Member mA;
    Member mB;
public:
    Test() : mB("mB"), mA("mA")
    {
        printf("Test()\n");
    }
    ~Test()
    {
        printf("~Test()\n");
    }
};

Member gA("gA");

int main(int argc, char *argv[])
{
    Test test;
    return 0;
}

對象定義時會申請對象空間并調用構造函數。
對象聲明告訴編譯器存在一個對象。

8、構造函數拋出異常

如果構造函數中拋出異常,構造過程立即停止,當前對象無法生成,析構函數不會被調用,對象占用的空間被立即收回。當構造函數中可能拋出異常時使用二階構造模式。

#include <iostream>

using namespace std;

class Test
{
public:
    Test()
    {
        cout << "Test()" << endl;
        throw 0;
    }
};

int main(int argc, char *argv[])
{
    Test* p = reinterpret_cast<Test*>(0x1);
    try
    {
        p = new Test();
    }
    catch(...)
    {
        cout << "Exception..." << endl;
    }
    cout << "p = " << p << endl;//p = 0x1
    return 0;
}

二、拷貝構造函數(copy constructor)

1、拷貝構造函數簡介

拷貝構造函數根據己存在的對象創建新對象,新對象不由構造函數來構造,而是由拷貝構造函數來完成。

2、拷貝構造函數的定義

拷貝構造函數的聲明如下:
classname(const classname& xxx);
拷貝構造函數是參數為const classname&的構造函數。當類中沒有定義拷貝構造函數時,編譯器會提供一個默認拷貝構造函數,其函數體內會進行成員變量的淺拷貝。

#include <iostream>

using namespace std;

class Person
{
public:
    const char* name;
    int age;
    Person()
    {
    }
    void print()
    {
        printf("My name is %s, I'm is %d years old.\n",name,age);
    }
};

int main(int argc, char *argv[])
{
    Person p;
    p.name = "Bauer";
    p.age = 30;
    Person p1 = p;//編譯器提供默認的拷貝構造函數
    p1.print();
    return 0;
}

上述代碼中,編譯器會為類提供一個默認的拷貝構造函數。

#include <iostream>

using namespace std;

class Person
{
public:
    const char* name;
    int age;
    void print()
    {
        printf("My name is %s, I'm is %d years old.\n",name,age);
    }
};

int main(int argc, char *argv[])
{
    Person p;//編譯器提供默認的無參構造函數
    p.name = "Bauer";
    p.age = 30;
    Person p1 = p;//編譯器提供默認的拷貝構造函數
    p1.print();
    return 0;
}

上述代碼中,編譯器會為類提供一個默認的無參構造函數和默認的拷貝構造函數。

3、拷貝構造函數的規則

拷貝構造函數的規則如下:
A、編譯器可以提供默認的拷貝構造函數。一旦類中定義了拷貝構造函數,編譯器不會為類再提供默認的拷貝構造函數。
B、編譯器提供的拷貝構造函數是等位拷貝,即淺拷貝。
C、要實現深拷貝,必須要自定義。
淺拷貝后對象的物理狀態相同,深拷貝后對象的邏輯狀態相同。
編譯器可以為類提供默認的拷貝構造函數,一旦類中定義了拷貝構造函數,編譯器將不會再為類提供默認拷貝構造函數和默認無參構造函數,因此開發者必須在類中自定義拷貝構造函數的同時自定義構造函數。編譯器提供的默認拷貝構造器是等位拷貝,即淺拷貝,如果類中包含的數據元素全部在棧上,淺拷貝也可以滿足需求的;如果類中有堆上的數據,則會發生多次析構行為。
淺拷貝只是對指針的拷貝,拷貝后兩個指針指向同一個內存空間,深拷貝不僅對指針進行拷貝,而且對指針指向的內容進行拷貝,經深拷貝后的指針是指向兩個不同地址的指針。
深拷貝構造函數的示例如下:

#include <iostream>
#include <string.h>

using namespace std;

class IntArray
{
public:
    IntArray(int len)
    {
        m_pointer = new int[len];
        for(int i=0; i<len; i++)
        {
            m_pointer[i] = 0;
        }
        m_length = len;
    }
    //深拷貝
    IntArray(const IntArray& obj)
    {
        m_length = obj.m_length;
        m_pointer = new int[obj.m_length];
        for(int i=0; i<obj.m_length; i++)
        {
            m_pointer[i] = obj.m_pointer[i];
        }
    }
    int length()
    {
        return m_length;
    }
    bool get(int index, int& value)
    {
        bool ret = (0 <= index) && (index < length());
        if( ret )
        {
            value = m_pointer[index];
        }
        return ret;
    }

    bool set(int index, int value)
    {
        bool ret = (0 <= index) && (index < length());
        if( ret )
        {
            m_pointer[index] = value;
        }
        return ret;
    }
    ~IntArray()
    {
        delete [] m_pointer;
    }
private:
    int m_length;
    int* m_pointer;
};

int main(int argc, char *argv[])
{
    IntArray array1(10);
    for(int i = 0; i < 10; i++)
    {
        array1.set(i,i*i);
    }
    IntArray array2 = array1;
    for(int i = 0; i < 10; i++)
    {
        int value;
        array2.get(i,value);
        cout << value << endl;
    }
    return 0;
}

類中未定義拷貝構造函數時,編譯器會提供淺拷貝的默認拷貝構造函數:

#include <iostream>
#include <string.h>

using namespace std;

class IntArray
{
public:
    IntArray(int len)
    {
        m_pointer = new int[len];
        for(int i=0; i<len; i++)
        {
            m_pointer[i] = 0;
        }
        m_length = len;
        cout << "Constructor" << endl;
    }

    int length()
    {
        return m_length;
    }
    bool get(int index, int& value)
    {
        bool ret = (0 <= index) && (index < length());
        if( ret )
        {
            value = m_pointer[index];
        }
        return ret;
    }

    bool set(int index, int value)
    {
        bool ret = (0 <= index) && (index < length());
        if( ret )
        {
            m_pointer[index] = value;
        }
        return ret;
    }

    ~IntArray()
    {
        cout << "DeConstructor" << endl;
        cout << m_pointer << endl;
        delete [] m_pointer;
        m_pointer = NULL;
    }
private:
    int m_length;
    int* m_pointer;
};

int main(int argc, char *argv[])
{
    IntArray array1(10);
    for(int i = 0; i < 10; i++)
    {
        array1.set(i,i*i);
    }
    IntArray array2(array1);
    for(int i = 0; i < 10; i++)
    {
        int value;
        array2.get(i,value);
        cout << value << endl;
    }

    return 0;
}

上述代碼中,編譯器提供的默認構造函數為淺拷貝,只會進行值拷貝。在對象銷毀時調用析構函數,會造成釋放同一內存空間2次,導致程序異常(經測試,Linux G++編譯器編譯后程序異常出錯,Windows下QtCreator+MinGW編譯器沒有異常出錯)。
需要深拷貝的場景一般是對象中有成員指向系統中的資源。

4、拷貝構造函數的調用

拷貝構造函數的調用場合如下:
A、創建對象的副本,如A a=b;A c(a)。
B、函數中以對象作為參數或返回值。

三、析構函數(destructor)

1、析構函數簡介

析構函數是C++語言中類的特殊的清理函數,在類對象銷毀時,自動調用,完成對象的銷毀。析構函數的作用,并不是刪除對象,而在對象銷毀前完成的一些清理工作,如釋放申請的系統資源。

2、析構函數的定義

析構函數的定義語法如下:

~classname()
{

}

3、析構函數的規則

析構函數的規則如下:
A、對象銷毀時,自動調用。完成銷毀的善后工作。
B、無返值 ,與類名同。無參,不可以重載,不能設置默認參數

4、析構函數的調用

析構函數的調用場景如下:
A、棧對象離開其作用域。
B、堆對象被手動delete。

5、析構函數的使用

當類中自定義了構造函數,并且構造函數中使用了系統資源如堆空間申請、文件打開等,需要自定義析構函數,在析構函數體內釋放使用的系統資源。

6、析構函數拋出異常

析構函數中拋出異常將導致對象使用的資源無法釋放。

四、this指針

this指針是編譯器在創建對象時,默認生成的指向當前對象的指針。
this指針作用如下:
A、避免構造器的參數與成員名相同。
B、基于this指針的自身引用還被廣泛地應用于那些支持多重串聯調用的函數中。比如連續賦值。
C、this指針是const類型的指針。

五、賦值操作符重載

1、賦值操作符重載簡介

C++編譯器默認為每個類重載了賦值操作符,默認的賦值操作符是淺拷貝的,因此當需要進行深拷貝時必須顯示重載賦值操作符。用一個己有對象,給另外一個已有對象賦值會調用賦值操作符重載函數。

2、賦值操作符重載函數定義

賦值操作符重載函數的聲明如下:
classname& operator=(const classname& another);
賦值操作符重載函數的實現如下:

classname& operator=(const classname& another)
    {   
    if(this != &another)
{
//資源分配操作和賦值拷貝
}
        return *this;
    }

3、賦值操作符重載的規則

賦值操作符重載的規則如下:
A、C++編譯器提供默認的賦值運算符重載。
B、C++編譯器提供的賦值操作符重載函數也是等位拷貝,即淺拷貝。
C、如果需要要深拷貝,必須自定義賦值操作符。
D、自定義面臨的問題有三個:自賦值、內存泄漏、重析構。
E、賦值操作符函數需要返回引用,且不能用const修飾,其目的是實現連續賦值。

4、賦值操作符重載示例

#include <iostream>

using namespace std;

class IntArray
{
public:
    IntArray(int num)
    {
        m_length = num;
        m_array = new int[num];
        for(int i = 0; i < num; i++)
        {
            m_array[i] = 0;
        }
    }
    ~IntArray()
    {
        if(m_array != NULL)
        {
            delete [] m_array;
            m_array = NULL;
        }
    }
    //拷貝構造函數(深拷貝)
    IntArray(const IntArray& another)
    {
        cout << "copy constructor." << endl;
        if(this != &another)
        {
            m_length = another.length();
            m_array = new int[another.length()];
            for(int i = 0; i < another.length(); i++)
            {
                m_array[i] = another.getValue(i);
            }
        }
    }
    //賦值操作符重載函數(深拷貝)
    IntArray& operator = (const IntArray& another)
    {
        cout << "assign function." << endl;
        if(this != &another)
        {
            delete m_array;
            m_length = another.length();
            m_array = new int[another.length()];
            for(int i = 0; i < another.length(); i++)
            {
                m_array[i] = another.getValue(i);
            }
        }
        return *this;
    }
    int length()const
    {
        return m_length;
    }
    int getValue(int index)const
    {
        return m_array[index];
    }
    void setValue(int index, int value)
    {
        m_array[index] = value;
    }
private:
    int* m_array;
    int m_length;
};

int main(int argc, char *argv[])
{
    IntArray array1(10);
    for(int i = 0; i < array1.length(); i++)
    {
        array1.setValue(i, i*i);
    }
    IntArray array2(5);
    array2 = array1;//賦值操作符
    for(int i = 0; i < array2.length(); i++)
    {
        cout << array2.getValue(i) <<endl;
    }
    IntArray array3 = array1;//拷貝構造函數
    for(int i = 0; i < array3.length(); i++)
    {
        cout << array3.getValue(i) <<endl;
    }
    return 0;
}

六、函數與類對象

1、函數傳值調用

類對象作為函數參數傳值調用函數時,會調用類的拷貝構造函數。

#include <iostream>

using namespace std;

class Test
{
public:
    Test(int a = 0, int b = 0)
    {
        cout << "call constructor." << endl;
        this->a = a;
        this->b = b;
    }
    Test(const Test& another)
    {
        cout << "copy constructor." << endl;
        if(this != &another)
        {
            a = another.a;
            b = another.b;
        }
    }
    void print()
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
    }
private:
    int a;
    int b;
};

void print(const Test obj)
{
    const_cast<Test&>(obj).print();
}

int main(int argc, char *argv[])
{
    Test test;//call constructor.
    print(test);//copy constructor.

    return 0;
}

2、函數傳引用調用

類對象作為函數參數傳引用調用函數時,不會調用類的拷貝構造函數。

#include <iostream>

using namespace std;

class Test
{
public:
    Test(int a = 0, int b = 0)
    {
        cout << "call constructor." << endl;
        this->a = a;
        this->b = b;
    }
    Test(const Test& another)
    {
        cout << "copy constructor." << endl;
        if(this != &another)
        {
            a = another.a;
            b = another.b;
        }
    }
    void print()
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
    }
private:
    int a;
    int b;
};

void print(const Test& obj)
{
    const_cast<Test&>(obj).print();
}

int main(int argc, char *argv[])
{
    Test test;//call constructor.
    print(test);

    return 0;
}

3、函數返回對象

#include <iostream>

using namespace std;

class Test
{
public:
    Test(int a = 0, int b = 0)
    {
        cout << "call constructor." << endl;
        this->a = a;
        this->b = b;
    }
    Test(const Test& another)
    {
        cout << "copy constructor." << endl;
        if(this != &another)
        {
            a = another.a;
            b = another.b;
        }
    }
    void print()
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
    }
private:
    int a;
    int b;
};

void print(const Test& obj)
{
    const_cast<Test&>(obj).print();
}

Test func(const Test& obj)
{
    return const_cast<Test&>(obj);
}

int main(int argc, char *argv[])
{
    Test test(1,2);//call constructor.
    func(test);//copy constructor.

    return 0;
}

上述代碼中,在調用func函數的棧上建立臨時空間,將test拷貝到臨時空間中,調用了拷貝構造函數。

4、函數返回引用

返回引用多用于產生串聯應用。比如連等式。棧對象是不可以返回引用的,除非函數的調用者返回自身對象。當函數返回引用類型時,沒有復制返回值,返回的是對象本身。當函數執行完畢時,將釋放分配給局部對象的存儲空間,對局部對象的引用就會指向不確定的內存。返回指向局部對象的指針也是一樣的,當函數結束時,局部對象被釋放,返回的指針就變成了不再存在的對象的懸垂指針。返回引用時,要求在函數的參數中包含有以引用方式或指針方式存在并且需要被返回的參數,即返回的引用不能是函數內部的局部棧對象。

#include <iostream>

using namespace std;

class Test
{
public:
    Test(int a = 0, int b = 0)
    {
        cout << "call constructor." << endl;
        this->a = a;
        this->b = b;
    }
    Test(const Test& another)
    {
        cout << "copy constructor." << endl;
        if(this != &another)
        {
            a = another.a;
            b = another.b;
        }
    }

    Test& operator =(const Test& another)
    {
        if(this != &another)
        {
            a = another.a;
            b = another.b;
        }
        return *this;
    }
    void print()
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
    }
private:
    int a;
    int b;
    friend Test& func(int a, int b, Test& obj);
};

Test& func(int a, int b, Test& obj)
{
    obj.a = a;
    obj.b = b;
    return obj;
}

int main(int argc, char *argv[])
{
    Test test(1,2);//call constructor.
    Test test1;//call constructor.
    test1 = test;
    test1.print();
    return 0;
}

上述代碼中,類的賦值操作符重載函數和func全局函數返回了引用。

七、棧和堆上的對象和對象數組

1、用new 和delete生成銷毀堆對象

使用new創建一個堆對象,會自動調用構造函數,delete銷毀一個堆對象對自動調用析構函數。

2、棧對象數組

如果生成的數組,未初始化,則必調用無參構造函數?;蚴謩诱{用帶參構造函數。

3、堆對象數組

如果生成的數組,未初始化,則必調用無參構造函數?;蚴謩诱{用帶參構造函數。
構造函數無論是重載還是默認參數,一定要把系統默認的無參構造函數包含進來。不然生成數組的時候,可能會有些麻煩。

八、類成員函數的存儲方式

1、類成員的組成

類成員包括成員變量和成員函數,每個對象所占用的存儲空間只是該對象的非靜態成員變量所占用的存儲空間,而不包括函數代碼、靜態成員變量所占用的存儲空間,成員函數存儲于代碼段,靜態成員變量存儲于全局數據區。

#include <iostream>

using namespace std;

class Test
{
public:
    Test(int a = 0, int b = 0, int c = 0)
    {
        this->a = a;
        this->b = b;
        this->c = c;
    }
    void print()
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
        cout << "c = " << c << endl;
    }
private:
    int a;
    int b;
    static int c;
};

int Test::c = 0;

int main(int argc, char *argv[])
{
    Test test(1,2,3);
    test.print();
    cout << "test has " << sizeof(test) << " bytes." << endl;//8

    return 0;
}

上述代碼中,一個Test對象占用空間由int類型的成員變量a和b決定,靜態成員變量c所占用空間不在Test對象中。

2、調用原理

所有對象都調用共用的函數代碼段,不同的對象調用函數時,this指針指向調用函數的對象,因此函數中對于類的數據成員的調用分別調用了對象的數據成員。
不論成員函數在類內定義還是在類外定義,成員函數的代碼段都用同一種方式存儲。

九、const修飾類對象

1、const修飾成員變量

const修飾類的成員變量,表示只讀變量,會被分配空間,不能被修改,只能在構造函數的初始化列表中初始化。const成員變量可以被const和非const成員函數調用,但不能被修改。

2、const修飾成員函數

const成員函數的定義:
Type classname::functioname(Type p)const;
const成員函數的聲明和實現必須要使用const關鍵字限制。
const成員函數內部只能調用const成員函數,不能修改成員變量的值。
const修飾函數構成重載時使用規則:
A、如果const函數構成函數重載,const對象只能調用const函數,非const對象優先調用非const函數。
B、const函數只能調用const函數。非const函數可以調用const函數。
C、類體外定義的const成員函數,在定義和聲明處都需要const修飾符。

3、const修飾類對象

const對象是只讀對象,只能調用const成員函數??稍L問const或非const的成員變量,但不能修改。const只讀對象是編譯時概念,運行時無效。

#include <iostream>

using namespace std;

class Test
{
public:
    int data;
public:
    Test(int i)
    {
        data = i;
        cout << "Test(int i): " << i << endl;
    }
    int getData()const
    {
        return data;
    }
    int getValue()
    {
        return data;
    }
    void setData(const int value)
    {
        data = value;
    }
    ~Test()
    {
       cout << "~Test()" << endl;
    }
};

int main(int argc, char *argv[])
{
    const Test ct(10);
    ct.getData();
    //ct.getValue();//error
    //error: passing 'const Test' as 'this' argument of 'void Test::getValue()' discards qualifiers
    int* p = const_cast<int*>(&ct.data);
    *p = 100;
    cout << ct.getData() << endl;//100
    return 0;
}

對象由成員變量和成員函數構成,成員變量可以位于棧、堆和全局數據區,成員函數只能位于代碼段。每一個對象擁有自己的成員變量,所有的對象共享類的成員函數,成員函數能直接訪問對象的成員變量。成員函數中隱藏this用于指代當前對象。

十、類與static修飾符

1、static修飾成員變量

在C++類中,可以定義靜態成員變量。使用static關鍵字對類的成員變量進行修飾時,可以得到類的靜態成員變量。
類的靜態成員變量的聲明如下:
static Type varname; //在類的內部
類的靜態成員變量的初始化如下:
Type classname::varname = initvalue; //在類的外部
類的靜態成員變量的使用方法如下:
A、類名::靜態數據成員
B、類對象.靜態數據成員
靜態成員變量的特性如下:
A、定義時使用static關鍵字修飾
B、靜態成員變量在類外單獨分配空間,類對象的大小不包括靜態成員變量
C、靜態成員變量在程序內部位于全局數據區
靜態成員變量屬于整個類,其生命周期不依賴于任何對象,可以通過類名直接訪問類的公有靜態成員變量,類的所有對象共享類的靜態成員變量,可以通過對象名訪問類的靜態成員變量。類的靜態成員變量只存儲一份供所有對象共用,在所有對象中都可以共享它。使用靜態成員變量實現多個對象之間的數據共享不會破壞隱藏(相比全局變量的優點)的原則,保證了安全性還可以節省內存。
類的靜態成員,屬于類,也屬于對象,但終歸屬于類。

#include <iostream>

using namespace std;

class Test
{
private:
    static int data;
public:
    Test()
    {
        data++;
    }
    int count()const
    {
        return data;
    }
    ~Test()
    {
       data--;
    }
};

//在全局數據區分配靜態成員變量空間并初始化
int Test::data = 0;

int main(int argc, char *argv[])
{
    Test test1;
    Test test2;
    cout << test1.count() << endl;
    return 0;
}

2、static修飾成員函數

為了管理靜態成員,C++提供了靜態函數,提供對外接口。靜態成員函數只能訪問靜態成員變量。
靜態成員函數的聲明如下:
static Type functionname(parameters);
靜態成員函數的特性如下:
A、靜態成員函數的意義,不在于信息共享、數據溝通,而在于管理靜態數據成員,完成對靜態數據成員的封裝。
B、靜態成員函數只能訪問靜態數據成員。原因:非靜態成員函數,在調用時 this指針時被當作參數傳進。而靜態成員函數屬于類,而不屬于對象,沒有this指針。
C++語言中類與對象是什么
靜態成員函數的使用如下:
A、類名::函數調用
B、類對象.函數調用

#include <iostream>

using namespace std;

class Test
{
private:
    static int data;
public:
    Test()
    {
        data++;
    }
    //靜態成員函數
    static int count()
    {
        return data;
    }
    ~Test()
    {
       data--;
    }
};

//在全局數據區分配靜態成員變量空間并初始化
int Test::data = 0;

int main(int argc, char *argv[])
{
    cout << Test::count() << endl;//0
    Test test;
    cout << test.count() << endl;//1
    return 0;
}

3、static const修飾符

如果一個類的成員變量,既要實現共享,又要實現不可改變,可以使用static const修飾。static const修飾成員變量時,既可以在類內部進行初始化,也可以在類外進行初始化。
static const修飾成員函數,是靜態成員函數。

#include <iostream>

using namespace std;

class Test
{
public:
    static const int data;
//static const int data = 0;
public:
    Test()
    {
    }
    //靜態成員函數
    static const int count()
    {
        return data;
    }
    ~Test()
    {
    }
};
const int Test::data = 100;

int main(int argc, char *argv[])
{

    cout << Test::count() << endl;//100
    Test test;

    cout << test.count() << endl;//100
    cout << Test::data << endl;//100
    cout << test.data << endl;//100
    return 0;
}

十一、指向類成員的指針

在C++語言中,可以定義一個指針,使其指向類成員或成員函數,然后通過指針來訪問類的成員。包括指向成員變量的指針和指向成員函數的指針。

1、指向類成員變量的指針

指向類的成員變量的指針定義如下:
<br/>&lt;數據類型&gt;&lt;類名&gt;::*&lt;指針名&gt;<br/>
指向類的成員變量的指針的賦值與初始化如下:
<br/>&lt;數據類型&gt;&lt;類名&gt;::*&lt;指針名&gt;[=&&lt;類名&gt;::&lt;非靜態數據成員&gt;]<br/>
指向非靜態數據成員的指針在定義時必須和類相關聯,在使用時必須和具體的對象關聯。
指向類的成員變量的指針的解引用如下:

<類對象名>.*<指向非靜態數據成員的指針>
<類對象指針>->*<指向非靜態數據成員的指針>

由于類不是運行時存在的對象。因此,在使用這類指針時,需要首先指定類的一個對象,然后,通過對象來引用指針所指向的成員。

#include <iostream>

using namespace std;

class Student
{
public:
    Student(string n, int nu):name(n),num(nu){}
    string name;
    int num;
};

int main(int argc, char *argv[])
{
    Student s("zhangsan",1002);
    Student s2("lisi",1001);
    string Student::*ps = &Student::name;
    int Student::*pi = &Student::num;
    cout<<s.*ps<<endl;
    cout<<s.*pi<<endl;
    cout<<s2.*ps<<endl;
    cout<<s2.*pi<<endl;
    Student *pp = new Student("wangwu",1003);
    cout<<pp->*ps<<endl;
    cout<<pp->*pi<<endl;
    return 0;
}

2、指向類成員函數的指針

定義一個指向非靜態成員函數的指針必須在三個方面與其指向的成員函數保持一致:參數列表要相同、返回類型要相同、所屬的類型要相同。
A、定義

<數據類型>(<類名>::*<指針名>)(<參數列表>)

B、賦值與初始化

<數據類型>(<類名>::*<指針名>)(<參數列表>)[=&<類名>::<非靜態成員函數>]

C、解引用

(<類對象名>.*<指向非靜態成員函數的指針>)(<參數列表>)
(<類對象指針>->*<指向非靜態成員函數的指針>)(<參數列表>)

由于類不是運行時存在的對象。因此,在使用這類指針時,需要首先指定類的一個對象,然后,通過對象來引用指針所指向的成員。
指向非靜態成員函數時, 必須用類名作限定符, 使用時則必須用類的實例作限定符。指向靜態成員函數時,則不需要使用類名作限定符。

#include <iostream>

using namespace std;

class Student
{
public:
    Student(string n, int nu)
    {
        name = n;
        num = nu;
    }
    void print()
    {
        cout << "name :" << name <<endl;
        cout << "num: " << num << endl;
    }
private:
    string name;
    int num;
};

int main(int argc, char *argv[])
{
    Student s1("zhangsan",1002);
    Student s2("lisi",1001);
    Student *s3 = new Student("wangwu",1003);
    void (Student::*pfunc)() = &Student::print;
    (s1.*pfunc)();
    (s2.*pfunc)();
    (s3->*pfunc)();
    return 0;
}

3、指向類靜態成員的指針

A、指向類靜態數據成員的指針
指向靜態數據成員的指針的定義和使用與普通指針相同,在定義時無須和類相關聯,在使用時也無須和具體的對象相關聯。
B、指向類靜態成員函數的指針
指向靜態成員函數的指針和普通指針相同,在定義時無須和類相關聯,在使用時也無須和具體的對象相關聯。

#include <iostream>

using namespace std;

class Test
{
public:
    Test()
    {
        count++;
    }
    static void print()
    {
        cout << "count: " << count << endl;
    }
    ~Test()
    {
        count--;
    }
    static int count;
};

int Test::count = 0;

int main(int argc, char *argv[])
{
    int* pc = &Test::count;
    void (*pfunc)() = &Test::print;
    cout << *pc << endl;
    pfunc();
    Test test1;
    Test test2;
    cout << *pc << endl;
    pfunc();
    return 0;
}

十二、類成員函數的重載

1、函數重載的特點

重載函數的特點如下:
A、重載函數的本質為多個不同的函數
B、重載函數的函數名和參數列表是唯一標識符
C、函數重載必須發生在同一個作用域中

2、類成員函數重載的規則

類成員函數重載的規則如下:
A、類的成員函數只能與類內的成員函數構成重載
B、類的成員函數不能與全局函數或其它類的成員函數構成重載

#include <iostream>

using namespace std;

class Test
{
private:
    int i;
    int j;
public:
    Test(int i = 0,int j = 0)
    {
        this->i = i;
        this->j = j;
    }
    //成員函數重載
    int sum()
    {
        return i + j;
    }
    static int sum(int i, int j)
    {
        return i + j;
    }
    static double sum(double i, double j)
    {
        return i + j;
    }
    ~Test()
    {
    }
};
//全局函數的重載
int sum(int a, int b)
{
    return a + b;
}
double sum(double a, double b)
{
    return a + b;
}

int main(int argc, char *argv[])
{
    cout << "member function overload." << endl;
    cout << Test::sum(2,10) << endl;
    cout << Test::sum(2.14,10.0) << endl;
    Test test1;
    cout << test1.sum() << endl;
    Test test2(100,200);
    cout << test2.sum() << endl;
    cout << test2.sum(1,9) << endl;
    cout << test2.sum(2.1,3.14) << endl;
    cout << "global function overload." << endl;
    cout << sum(10, 12) <<endl;
    cout << sum(3.14, 3.14) <<endl;

    return 0;
}

3、函數重載的意義

函數重載的意義如下:
A、通過函數名對函數功能進行提示
B、通過參數列表對函數用法進行提示
C、對已經存在的函數進行功能擴展

感謝各位的閱讀!關于“C++語言中類與對象是什么”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

c++
AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女