C++_構造函數與析構函數

来源:https://www.cnblogs.com/whale90830/archive/2019/03/12/10520316.html
-Advertisement-
Play Games

構造函數與析構函數 OOP第二課 1 構造函數 1.1 構造函數具有一些特殊的性質 1.2 定義構造函數的一般形式 1.3 利用構造函數創建對象 2 成員初始化表 3 預設參數的構造函數 4 重載構造函數 5 拷貝構造函數 5.1 自定義拷貝構造函數 5.2 預設的拷貝構造函數 5.3 調用拷貝構造 ...


構造函數與析構函數

OOP第二課

  • 1 構造函數
    • 1.1 構造函數具有一些特殊的性質
    • 1.2 定義構造函數的一般形式
    • 1.3 利用構造函數創建對象
  • 2 成員初始化表
  • 3 預設參數的構造函數
  • 4 重載構造函數
  • 5 拷貝構造函數
    • 5.1 自定義拷貝構造函數
    • 5.2 預設的拷貝構造函數
    • 5.3 調用拷貝構造函數的三種情況
    • 5.4 淺拷貝和深拷貝
  • 6 析構函數
  • 7 調用構造函數和析構函數的順序
  • 8 對象的生存期

構造函數和析構函數都是類的成員函數,但它們都是特殊的成員函數,執行特殊的功能,不用調用便自動執行,而且這些函數的名字類的名字有關。

C++語言中有一些成員函數性質是特殊的,這些成員函數負責對象的建立、刪除。這些函數的特殊性在於可以由編譯器自動地隱含調用,其中一些函數調用格式採用運算符函數重載的語法。

C++引進一個自動完成對象初始化過程的機制,這就是類的構造函數。

對象的初始化

  1. 數據成員是不能在聲明類時初始化
  2. 類型對象的初始化方法:
    1) 調用對外介面(public成員函數)實現:聲明類→定義對象→調用介面給成員賦值
    2) 應用構造函數(constructor) 實現:聲明類→定義對象→同時給成員賦值

1. 構造函數

構造函數是一種特殊的成員函數,它主要用於為對象分配空間,進行初始化。

1.1 構造函數具有一些特殊的性質:

  • (1) 構造函數的名字必須與類名相同
  • (2) 構造函數可以有任意類型的參數,但不能指定返回類型。 它有隱含的返回值,該值由系統內部使用。
  • (3) 構造函數是特殊的成員函數,函數體可寫在類體內,也可寫在類體外。
  • (4) 構造函數可以重載,即一個類中可以定義多個參數個數或參數類型不同的構造函數。構造函數是不能繼承
  • (5) 構造函數被聲明為公有函數,但它不能像其他成員函數那樣被顯式地調用,它是在定義對象的同時被調用的。
  • (6) 在聲明類時如果沒有定義類的構造函數,編譯系統就會在編譯時自動生成一個預設形式的構造函數,
  • (7) 預設構造函數是構造對象時不提供參數的構造函數。
  • (8) 除了無參數構造函數是預設構造函數外,帶有全部預設參數值的構造函數也是預設構造函數。
  • (9) 自動調用:構造函數在定義類對象時自動調用, 不需用戶調用,也不能被用戶調用。在對象使用前調用。
  • (10) 調用順序:在對象進入其作用域時(對象使用前) 調用構造函數。

1.2 定義構造函數的一般形式

class 類名
{
public:
    類名(形參表) ; //構造函數的原型
    //類的其它成員
};
類名::類名(形參表) //構造函數的實現
{
    //函數體
}

1.3 利用構造函數創建對象

  • (1) 利用構造函數直接創建對象.其一般形式為:類名 對象名[(實參表)];

這裡的“類名”與構造函數名相同,“實參表”是為構造函數提供的實際參數。

例2.7 為類Date建立一個構造函數
#include <iostream.h>
class Date {
public:
    Date(int y,int m,int d); // 構造函數
    void setDate(int y,int m,int d);
    void showDate();
private:
    int year, month, day;
};
Date::Date(int y,int m,int d) // 構造函數的實現
{ year=y; month=m; day=d; }
void Date::setDate(int y,int m,int d)
{ year=y; month=m; day=d; }
inline void Date::showDate()
{ cout<<year<<"."<<month<<"."<<day<<endl; }
例2.8 利用構造函數直接創建對象
#include <iostream.h>
class Date {
// 省略, 同例2.7
};
// 省略, 同例2.7
void main()
{
    Date date1(1998,4,28); // 定義類Date的對象date1,
    // 自動調用date1的構造函數,初始化對象date1
    cout<<"Date1 output1:"<<endl;
    date1.showDate(); // 調用date1的showDate(),顯示date1的數據
    date1.SetDate(2002,11,14); // 調用date1的setDate(),
    // 重新設置date1的數據
    cout<<"Date1 output2:"<<endl;
    date1.showDate(); // 調用date1的showDate(),顯示date1的數據
}
constructing...
Date1 output1:
1998.4.28
Date1 output2:
2002.11.14
  • (2) 利用構造函數創建對象時,通過指針和new來實現。其一般語法形式為:
    類名 *指針變數 = new 類名[(實參表)];
void main()
{
    Date *date1;
    date1=new Date(1998,4,28);
    //可合寫成:Date *date1=new Date(1998,4,28);
    cout<<"Date1 output1:"<<endl;
    date1->showDate();
    date1->setDate(2002,11,14);
    cout<<"Date1 output2:"<<endl;
    date1->showDate();
    delete date1;
}

說明:

  • 構造函數的名字必須與類名相同,否則編譯器將把它當作一般的成員函數來處理。
  • 構造函數是不能說明它的返回值類型的,甚至說明為void類型也不行。
  • 構造函數可以是不帶參數的。
class A{
public:
    A();
    //…
private:
    int x;
};
A∷A()
{
    cout<<"initialized \n";
    x=50;
}
main()
{
    A a;
    …
}

有兩個長方柱,其長、寬、高分別為:(1)12,25,30;(2)15,30,21。求它們的體積。要求:編一個基於對象的程式,在類中用帶參數的構造函數。

class Box{
public:
    Box(int,int,int);
    int volume( );
private:
    int height;
    int width;
    int length;
};
Box::Box(int h,int w,int len)
{
    height = h;
    width = w;
    length = len;
}
int Box::volume( )
{
    return height*width*length;
}
int main( )
{
    Box box1(12,25,30);
    cout << box1.volume( ) << endl;
    Box box2(15,30,21);
    cout << box2.volume( ) << endl;
    return 0;
}

2. 成員初始化表

對於常量類型引用類型的數據成員,不能在構造函數中用賦值語句直接賦值,C++提供初始化表進行置初值。

帶有成員初始化表的構造函數的一般形式如下:

類名::構造函數名([參數表])[:(成員初始化表)]
{
// 構造函數體
}

成員初始化表的一般形式為:

數據成員名1(初始值1),數據成員名2(初始值2),……

例2.9 成員初始化表的使用
#include<iostream.h>
class A{
public:
    A(int x1):x(x1),rx(x),pi(3.14) // rx(x)相當於rx=x, pi(3.14)相當於pi=3.14
    { }
    void print()
    {cout<<"x="<<x<<" "<<"rx="<<rx<<" "<<"pi="<<pi;}
private:
    int x; int& rx; const float pi;
};
main()
{
    A a(10);
    a.print();
    return 0;
}
  • 構造函數採用成員初始化表對數據成員進行初始化,是一些程式員喜歡使用的方法。
class B{
    int i;
    char j;
    float f;
public:
    B(int I, char J, float F)
    { i=I; j=J; f=F; };
};
class B{
public:
    B(int I,char J,float F):i(I),j(J),f(F)
    { }
private:
    int i;
    char j;
    float f;
};

說明

如果需要將數據成員存放在堆中或數組中,則應在構造函數中使用賦值語句,即使構造函數有成員初始化表也應如此。

class C{
public:
    C(int I,char Ch,float F,char N[]):i(I),ch(Ch),f(F)
    { strcpy (name,N);}
private:
    int i;
    char ch;
    float f;
    char name[25];
};
  • 類成員是按照它們在類里被聲明的順序初始化的,與它們在初始化表中列出的順序無關。
【例2.10】
#include<iostream.h>
class D {
public:
    D(int i):mem2(i),mem1(mem2+1)
    {
        cout<<"mem1: "<<mem1<<endl;
        cout<<"mem2: "<<mem2<<endl;
    }
private:
    int mem1;
    int mem2;
};
void main()
{
    D d(15);
}
mem1: -858993459
mem2: 15

3. 預設參數的構造函數

例2.11
#include<iostream.h>
class Coord {
public:
    Coord(int a=0,int b=0){ x=a; y=b;} // 帶有預設參數的構造函數
    int getx(){ return x; }
    int gety(){ return y; }
private:
    int x,y;
};
void main()
{
    Coord op1(5,6); Coord op2(5); Coord op3;
    int i,j;
    i=op1.getx();j=op1.gety();
    cout<<"op1 i= "<<i<<" op1 j= "<<j<<endl;
    i=op2.getx();j=op2.gety();
    cout<<"op2 i= "<<i<<" op2 j= "<<j<<endl;
    i=op3.getx();j=op3.gety();
    cout<<"op3 i= "<<i<<" op3 j= "<<j<<endl;
}
class Box{
public:
    Box(int h=10,int w=10,int l=10); //在聲明構造函數時指定預設參數
    int volume( ){ return(height*width*length); }
private:
    int height;
    int width;
    int length;
};
Box:: Box(int h,int w,int l) //在定義函數時可以不指定預設參數
{
    height=h;
    width=w;
    length=l;
}

4. 重載構造函數

構造函數的重載

在一個類中可以定義多個構造函數,以便對類對象提供不同的初始化的方法,供用戶選用。這些構造函數具有相同的名字,而參數的個數或參數的類
型不相同(這稱為構造函數的重載)

關於構造函數重載的說明

  • (1) 預設構造函數:一個調用構造函數時不必給出實參的構造函數。 顯然,無參的構造函數屬於預設構造函數。一個類只能有一個預設構造函數。
  • (2) 儘管在一個類中可以包含多個構造函數,但是對於每一個對象來說,建立對象時只執行其中一個構造函數,並非每個構造函數都被執行。
class Box{
public:
    Box(int h, int w, int l): height(h),width(w),length(l) { }
    Box();
    int volume( );
private:
    int height;
    int width;
    int length;
};
Box::Box()
{
    height = 10;
    width = 10;
    lenght = 10;
}
int Box::volume( )
{
    return height*width*length;
}
int main( )
{
    Box box1; // 書上為 box1();
    cout << box1.volume( ) << endl;
    Box box2(15,30,25);
    cout << box2.volume( ) << endl;
    return 0;
}
例2.17 重載構造函數應用常式。
#include <iostream.h>
class Date{
public:
    Date(); // 無參數的構造函數
    Date(int y,int m,int d); // 帶有參數的構造函數
    void showDate();
private:
    int year, month, day;
};
Date::Date()
{ year=1998; month=4; day=28; }
Date::Date( int y, int m, int d)
{ year=y; month=m; day=d; }
inline void Date::showDate()
{ cout<<year<<"."<<month<<"."<<day<<endl; }
void main()
{
    Date date1; // 聲明類Date的對象date1,
    // 調用無參數的構造函數
    cout<<"Date1 output: "<<endl;
    date1.showDate(); // 調用date1的showDate(),顯示date1的數據
    Date date2(2002, 11, 14); // 定義類Date的對象date2,
    // 調用帶參數的構造函數
    cout<<"Date2 output: "<<endl;
    date2.showDate(); // 調用date2的showDate(),顯示date2的數據
}

運行結果:

Date1 output:
1998.4.28
Date2 output:
2002.11.14
例2.18 關於計時器的例子
#include<iostream.h>
#include<stdlib.h>
class timer{
public:
    timer() // 無參數構造函數,給seconds清0
    { seconds=0; }
    timer(char* t) // 含一個數字串參數的構造函數
    { seconds=atoi(t); }
    timer(int t) // 含一個整型參數的構造函數
    { seconds=t; }
    timer(int min,int sec) // 含兩個整型參數的構造函數
    { seconds=min*60+sec; }
    int gettime()
    { return seconds; }
private:
    int seconds;
};
main()
{
    timer a,b(10),c("20"),d(1,10);
    cout<<"seconds1="<<a.gettime()<<endl;
    cout<<"seconds2="<<b.gettime()<<endl;
    cout<<"seconds3="<<c.gettime()<<endl;
    cout<<"seconds4="<<d.gettime()<<endl;
    return 0;
}
class x {
public:
    x(); // 沒有參數的構造函數
    x(int i=0); // 帶預設參數的構造函數
};
//…
void main()
{
    x one(10); // 正確,調用x(int i=0)
    x two; // 存在二義性
    //…
}

5. 拷貝構造函數

拷貝構造函數是一種特殊的構造函數,其形參是本類對象的引用。其作用是使用一個已經存在的對象去初始化另一個同類的對象。

  • 通過等於號複製對象時,系統會自動調用拷貝構造函數。
  • 拷貝構造函數與原來的構造函數實現了函數的重載。

拷貝構造函數具有以下特點:

  • (1) 因為該函數也是一種構造函數,所以其函數名與類名相同,並且該函數也沒有返回值類型。
  • (2) 該函數只有一個參數,並且是同類對象的引用
  • (3) 每個類都必須有一個拷貝構造函數。程式員可以根據需要定義特定的拷貝構造函數,以實現同類對象之間數據成員的傳遞。如果程式員沒有定義類的拷貝構造函數,系統就會自動生成產生一個預設的拷貝構造函數。

5.1 自定義拷貝構造函數

自定義拷貝構造函數的一般形式如下:

class 類名{
public :
    類名(形參); //構造函數
    類名(類名 &對象名); //拷貝構造函數
...
};
類名:: 類名(類名 &對象名) //拷貝構造函數的實現
{ 函數體 }
用戶自定義拷貝構造函數
class Coord{
    int x,y;
public:
    Coord(int a, int b) // 構造函數
    {
        x=a;
        y=b;
        cout<<"Using normal constructor\n";
    }
    Coord(const Coord& p) // 拷貝構造函數
    {
        x=2*p.x;
        y=2*p.y;
        cout<<"Using copy constructor\n";
    }
    //…
};

如果p1、 p2為類Coord的兩個對象,p1已經存在,則coord p2(p1)調用拷貝構造函數來初始化p2

例2.19 自定義拷貝構造函數的使用
#include<iostream.h>
class Coord {
public:
    Coord(int a,int b) // 構造函數
    { x=a; y=b; cout<<"Using normal constructor\n";}
    Coord(const Coord& p) // 拷貝構造函數
    { x=2*p.x; y=2*p.y; cout<<"Using copy constructor\n";}
    void print(){ cout<<x<<" "<<y<<endl; }
private:
    int x,y;
};
main()
{
    Coord p1(30,40); // 定義對象p1,調用了普通的構造函數
    Coord p2(p1); // 以“代入” 法調用拷貝構造函數,用對象p1初始化對象p2
    p1.print();
    p2.print();
    return 0;
}

除了用代入法調用拷貝構造函數外,還可以採用賦值法調用拷貝構造函數,如:

main()
{
    Coord p1(30,40);
    Coord p2=p1; //以"賦值"法調用拷貝構造函數,
    用對象p1初始化對象p2
    //…
}

5.2 預設的拷貝構造函數

如果沒有編寫自定義的拷貝構造函數,C++會自動地將一個已存在的對象複製給新對象,這種按成員逐一複製的過程由是預設拷貝構造函數自動完成的。

例2.20 調用預設的拷貝構造函數
#include<iostream.h>
class Coord{
public:
    Coord(int a,int b)
    { x=a; y=b; cout<<"Using normal constructor\n"; }
    void print(){ cout<<x<<" "<<y<<endl;}
private:
    int x,y;
};
main()
{
    Coord p1(30,40); // 定義類Coord的對象p1,
    // 調用了普通構造函數初始化對象p1
    Coord p2(p1); // 以“代入”法調用預設的拷貝構造函數,
    // 用對象p1初始化對象p2
    Coord p3=p1; // 以“賦值”法調用預設的拷貝構造函數,
    // 用對象p1初始化對象p3
    p1.print(); p2.print(); p3.print();
    return 0;
}

5.3 調用拷貝構造函數的三種情況

  • (1) 當用類的一個對象去初始化該類的另一個對象時。
Coord p2(p1); // 用對象p1初始化對象p2, 拷貝構造函數被調用(代入法)
Coord p3=p1; // 用對象p1初始化對象p3, 拷貝構造函數被調用(賦值法)
  • (2) 當函數的形參是類的對象,調用函數,進行形參和實參結合時。
//…
fun1(Coord p) // 函數的形參是類的對象
{
    p.print();
}
main()
{
    Coord p1(10,20);
    fun1(p1); // 當調用函數,進行形參和實參結合時,
    調用拷貝構造函數
    return 0;
}
  • (3) 當函數的返回值是對象,函數執行完成,返回調用者時。
// …
Coord fun2()
{
    Coord p1(10,30);
    return p1;
} // 函數的返回值是對象
main()
{
    Coord p2;
    P2=fun2(); // 函數執行完成,返回調用者時,調用拷貝構造函數
    return 0;
}

5.4 淺拷貝和深拷貝

所謂淺拷貝,就是由預設的拷貝構造函數所實現的數據成員逐一賦值,若類中含有指針類型數據, 則會產生錯誤

為瞭解決淺拷貝出現的錯誤,必須顯示地定義一個自己的拷貝構造函數,使之不但拷貝數據成員,而且為對象1和對象2分配各自的記憶體空間,這就是所謂的深拷貝

例2.23 淺拷貝例子
#include<iostream.h>
#include<string.h>
class Student {
public:
    Student(char *name1,float score1);
    ~Student();
private:
    char *name; // 學生姓名
    float score; // 學生成績
};
Student∷Student(char *name1,float score1)
{
    cout<<"Constructing..."<<name1<<endl;
    name=new char[strlen(name1)+1];
    if (name !=0)
    {
        strcpy(name,name1);
        score=score1;
    }
}
Student∷~Student()
{
    cout<<"Destructing..."<<name<<endl;
    name[0]='\0';
    delete name;
}
void main()
{
    Student stu1("liming",90); // 定義類Student的對象stu1
    Student stu2=stu1; // 調用預設的拷貝構造函數
}
Constructing... liming
Destructing... liming
Destructing...

淺拷貝示意圖

image

例2.24 深拷貝例子
#include<iostream.h>
#include<string.h>
class Student {
private:
    char *name; // 學生姓名
    float score; // 學生成績
public:
    Student(char *name1,float score1);
    Student(Student& stu);
    ~Student();
};
Student∷Student(char *name1,float score1)
{
    cout<<"constructing..."<<name1<<endl;
    name=new char[strlen(name1)+1];
    if (name !=0)
    {
        strcpy(name,name1);
        score=score1;
    }
}
Student∷Student(Student& stu)
{
    cout<<"Copy constructing..."<<stu.name<<endl;
    name=new char[strlen(stu.name)+1];
    if (name !=0)
    {
        strcpy(name,stu.name);
        score=stu.score;
    }
}
Student∷~Student()
{
    cout<<"Destructing..."<<name<<endl;
    name[0]='\0';
    delete name;
}
void main()
{
    Student stu1("liming", 90); // 定義類Student的對象stu1,
    Student stu2=stu1; // 調用自定義的拷貝構造函數
}
Constructing…liming
Copy constructing…liming
Destructing…liming
Destructing…liming

深拷貝示意圖

image

6. 析構函數

析構函數也是一種特殊的成員函數。它執行與構造函數相反的操作,通常用於撤消對象時的一些清理任務,如釋放分配給對象的記憶體空間等。

析構函數有以下一些特點:

  • ① 析構函數與構造函數名字相同,但它前面必須加一個波浪號(~);
  • ② 析構函數沒有參數,也沒有返回值,而且不能重載。因此在一個類中只能有一個析構函數;
  • ③ 當撤消對象時,編譯系統會自動地調用析構函數。 如果程式員沒有定義析構函數,系統將自動生成和調用一個預設析構函數,預設析構函數只能釋放對象的數據成員所占用的空間,但不包括堆記憶體空間。
例2.25 重新說明類Date
#include <iostream.h>
class Date{
public:
    Date(int y,int m,int d); // 構造函數
    ~Date(); // 析構函數
    void setDate(int y,int m,int d);
    void showDate();
private:
    int year, month, day;
};
Date::Date(int y,int m,int d) // 構造函數的實現
{
    cout<<"constructing..."<<endl;
    year=y;month=m; day=d;
}
Date::~Date() // 析構函數的實現
{ cout<<"destruting..."<<endl; }
void Date::setDate(int y,int m,int d)
{ year=y;month=m;day=d; }
inline void Date::showDate()
{ cout<<year<<"."<<month<<"."<<day<<endl; }
void main()
{
    Date date1(1998,4,28); // 定義類Date的對象date1,
    // 調用date1的構造函數,初始化對象date1
    cout<<"Date1 output1:"<<endl;
    date1.showDate(); // 調用date1的showDate(),顯示date1的數據
    date1.setDate(2002,11,14); // 調用date1的setDate(),
    // 重新設置date1的數據
    cout<<"Date1 output2:"<<endl;
    date1.showDate(); // 調用date1的showDate(),顯示date1的數據
}

析構函數被調用的兩種情況

1) 若一個對象被定義在一個函數體內,當這個函數結束時,析構函數被自動調用。
2) 若一個對象是使用new運算符動態創建,在使用delete釋放時,自動調用析構函數。

【例2.13】 較完整的學生類例子
#include<iostream.h>
#include<string.h>
class Student {
public:
    Student(char *name1,char *stu_no1,float score1); // 構造
    函數
    ~Student(); // 析構函數
    void modify(float score1); // 修改數據
    void show(); // 顯示數據
private:
    char *name; // 學生姓名
    char *stu_no; // 學生學號
    float score; // 學生成績
};
Student∷Student(char *name1,char *stu_no1,float score1)
{
    name=new char[strlen(name1)+1];
    strcpy(name,name1);
    stu_no=new char[strlen(stu_no1)+1];
    strcpy(stu_no,stu_no1);
    score=score1;
}
Student∷~Student()
{
    delete []name;
    delete []stu_no;
}
void Student∷modify(float score1)
{ score=score1; }
void Student∷show()
{
    cout<<"\n name: "<<name;
    cout<<"\n stu_no: "<<stu_no;
    cout<<"\n score: "<<score;
}
void main()
{
    Student stu1("Liming","990201",90); // 定義類Student的對象stu1,
    // 調用stu1的構造函數,初始化對象stu1
    stu1.show(); // 調用stu1的show(),顯示stu1的數據
    stu1.modify(88); // 調用stu1的modify(),修改stu1的數據
    stu1.show(); // 調用stu1的show(),顯示stu1修改後的數據
}
name:Liming
stu_no:990201
score:90
name:Liming
stu_no:990201
score:88

預設的析構函數

每個類必須有一個析構函數。

若沒有顯式地為一個類定義析構函數,編譯系統會自動地生成一個預設的析構函數

其格式如下:類名::析構函數名( ){ }

class string_data {
public:
    string_data(char *)
    { str=new char[max_len];}
    ~string_data()
    { delete []str;}
    void get_info(char *);
    void sent_info(char *);
private:
    char *str;
    int max_len;
};

7. 調用構造函數和析構函數的順序

1) 一般順序

調用析構函數的次序正好與調用構造函數的次序相反:最先被調用的構造函數,其對應的(同一對象中的)析構函數最後被調用,而最後被調用的構造函數,其對應的析構函數最先被調用。

image

2) 全局對象

在全局範圍中定義的對象(即在所有函數之外定義的對象),它的構造函數在所有函數(包括main函數)執行之前調用。在程式的流程離開其作用域時(如main函數結束或調用exit函數)時,調用該全局對象的析構函數。

3) auto局部對象

局部自動對象(例如在函數中定義的對象),則在建立對象時調用其構造函數。如果函數被多次調用,則在每次建立對象時都要調用構造函數。在函數調用結束、對象釋放時先調用析構函數。

4) static局部對象

如果在函數中定義靜態局部對象,則只在程式第一次調用此函數建立對象時調用構造函數一次,在調用結束時對象並不釋放,因此也不調用析構函數,只在main函數結束或調用exit函數結束程式時,才調用析構函數。

8. 對象的生存期

對象按生存期的不同分為如下幾種:

(1) 局部對象
  • 當對象被定義時,調用構造函數,該對象被創建;當程式退出該對象所在的函數體或程式塊時,調用析構函數,對象被釋放。
  • 局部對象是被定義在一個函數體或程式塊內的,它的作用域限定在函數體或程式塊內,生存期較短。
(2) 全局對象
  • 當程式開始運行時,調用構造函數,該對象被創建;當程式結束時,調用析構函數,該對象被釋放。
  • 靜態對象是被定義在一個文件中,它的作用域從定義是起到文件結束時為止。生存期較長。
(3) 靜態對象
  • 當程式中定義靜態對象時,調用構造函數,該對象被創建;當整個程式結束時,調用析構函數,對象被釋放。
  • 全局對象是被定義在某個文件中,它的作用域包含在該文件的整個程式中,生存期是最長的。
(4) 動態對象
  • 執行new運算符調用構造函數,動態對象被創建;用delete釋放對象時,調用析構函數。
  • 動態對象是由程式員掌握的,它的作用域和生存期是由new和delete之間的間隔決定的。
類的應用舉例(例)

一圓形游泳池如圖所示,現在需在其周圍建一圓形過道,併在其四周圍上柵欄。柵欄價格為35元/米,過道造價為20元/平方米。過道寬度為3米,游泳池半徑由鍵盤輸入。要求編程計算並輸出過道和柵欄的造價。

#include <iostream>
using namespace std;
const float PI = 3.14159;
const float FencePrice = 35;
const float ConcretePrice = 20;
//聲明類Circle 及其數據和方法
class Circle{
private:
    float radius;
public:
    Circle(float r); //構造函數
    float Circumference() const; //圓周長
    /*函數後的修飾符const表示該成員函數的執行不會改變類的狀態,也就是說不會修改類的數據成員。 */
    float Area() const; //圓面積
};// 類的實現
// 構造函數初始化數據成員radius
Circle::Circle(float r)
{
    radius=r;
}
// 計算圓的周長
float Circle::Circumference() const
{
    return 2 * PI * radius;
}
// 計算圓的面積
float Circle::Area() const
{
    return PI * radius * radius;
}
void main ()
{
    float radius;
    float FenceCost, ConcreteCost;
    
    // 提示用戶輸入半徑
    cout<<"Enter the radius of the pool: ";
    cin>>radius;
    
    // 聲明 Circle 對象
    Circle Pool(radius);
    Circle PoolRim(radius + 3);
    
    //計算柵欄造價並輸出
    FenceCost=PoolRim.Circumference()*FencePrice;
    cout<<"Fencing Cost is ¥"<<FenceCost<<endl;
    
    //計算過道造價並輸出
    ConcreteCost=(PoolRim.Area()-
    Pool.Area())*ConcretePrice;
    cout<<"Concrete Cost is ¥"<<ConcreteCost<<endl;
}

運行結果

Enter the radius of the pool: 10
Fencing Cost is ¥2858.85
Concrete Cost is ¥4335.39

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 以前總是採用很Low的方式太同一臺伺服器上部署多個Web應用,步驟是這樣的:Copy Tomcat目錄-->更改conf/server.xml三個埠號 >部署war包 >分別啟動訪問。 這樣是最簡單的方式,但事實上是不推薦的,因為如果伺服器資源有限,需要部署的應用數目一旦增加,會給伺服器帶來 ...
  • Spring Cloud是什麼? Spring Cloud是一系列框架的有序集合。它利用Spring Boot的開發便利性巧妙地簡化了分散式系統基礎設施的開發,如服務發現註冊、配置中心、消息匯流排、負載均衡、斷路器、數據監控等,都可以用Spring Boot的開發風格做到一鍵啟動和部署。Spring並 ...
  • 題意 "鏈接" 長度為$n$的序列,用紅黃藍綠染色,其中紅黃只能是偶數,問方案數 Sol 生成函數入門題 任意的是$e^x$,偶數的是$\frac{e^x + e^{ x}}{2}$ 最後化完是$\frac{e^{4x} + 2e^{2x}+1}{4} = \frac{4^n+2 2^{n+1}}{ ...
  • 微信 什麼是微信也不多說,跟前面的支付寶一樣的 微信支付 微信支付也有個沙箱環境,沙箱環境官方文檔 由文檔中那句很顯眼的話所得,即使是測試環境也需要真實的商戶號,所以這個就沒法想支付寶那樣用沙箱賬號來演示了。至於為什麼沒有沙箱賬號這就不得而知了,想接入微信支付商戶的朋友,請移步先註... ...
  • Java Web工程連接MySQL資料庫 一、 準備工作 1.下載連接MySQL資料庫的JDBC (可以去官網下,也可以去百度雲找) 2.將下載的jar文件複製到Tomcat的lib目錄下 3.新建一個Web工程,將下載的jar文件複製過來 ps:第3步是直接複製jar文件,然後在粘貼在WEB IN ...
  • Note of Python Math math 庫是Python 提供的內置數學類函數庫,而其中複數類型常用於科學計算,一般計算並不常用,因此math 庫不支持複數類型。math 庫一共提供4個數學常數和44個函數(包括16個數值表示函數、8個冪對數函數、16個三角對數函數和4個高等特殊函數)。 ...
  • 類和對象 OOP第二課 1 類的構成 1.1 從結構到類 1.2 類的構成 2 成員函數的聲明 2.1 普通成員函數形式 2.2 將成員函數以內聯函數的形式進行說明 3 對象的定義和使用 3.1 對象的定義 3.2 對象中成員的訪問 3.3 類成員的訪問屬性 3.4 類中成員的訪問方式 3.5 對象 ...
  • 通過XML創建裝配bean 1.裝配不存在成員變數的bean <bean id="talent" class="cn.jqzhong.Spring.study.day2.LiteraryTalent"></bean> 2.裝配存在成員變數的bean 2.1.1屬性值為引用類型、引用類型列表 <bea ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...