學習C++這一篇就夠了(進階篇)

来源:https://www.cnblogs.com/wuyue525/archive/2023/07/07/17530738.html
-Advertisement-
Play Games

**在Python Web開發領域,Django框架的地位猶如璀璨的明星,其全面、高效和安全的特性使其在全球範圍內廣受歡迎。本文將全面解析Django框架的預設文件,並深入探討每個文件及其組成的意義和用途,透徹展示這個強大框架的文件結構和設計原理。** 首先,讓我們看一下創建一個新的Django項目 ...


記憶體模型

C++在執行程式的時候,將記憶體方向劃分為4個區域:

  • 代碼區:存放二進位代碼,由操作系統進行管理

  • 全局區:存放全局變數、靜態變數、常量,程式結束後由操作系統釋放

  • 棧區:存放函數參數、局部變數,由編譯器自動分配和釋放

  • 堆區:由開發者申請分配和釋放,若程式員不釋放,程式結束由操作系統自動回收

意義:對於不同區域存放的數據,賦予不同的生命周期,給編程更大的靈活性。

代碼區

存放CPU執行的二進位代碼(機器指令)

特點:

  • 共用:對於頻繁被執行的程式,只需要在記憶體中有一份就夠了

  • 只讀:防止被意外修改

全局區

  • 存放全局變數和靜態變數,還存放常量,包括字元串常量和其他常量

  • 數據在程式結束後由操作系統進行釋放

棧區

  • 存放函數參數、局部變數

  • 不要返回局部變數的地址,因為函數一執行完,棧區數據就被釋放了,雖然編譯器會做短暫的保留

堆區

  • 這是由開發者分配和釋放的,如果程式結束開發者不釋放,也會操作系統回收

  • 在C++中主要用new開闢堆區空間,用delete釋放

new 和 delete

new作用:用於讓開發者在堆區中開闢數據

delete作用:讓開發者手動釋放堆區數據

語法:

new 數據類型

delete 堆區地址

示例:

int *p = new int(8);  //在堆區開闢一個int類型的記憶體,存放數據8
int *p = new int[10]; //在堆區開闢一個int類型的記憶體存放數組,數組中有10個元素
delete(p);            //釋放地址a的數據

引用

作用:給變數起別名

語法:

數據類型 &別名 = 原名

註意事項:

  • 引用必須初始化

  • 初始化後,就不可以再發生改變了

  • 引用必須引一塊合法的記憶體空間,可以是棧區,可以是堆區,但不可以是自變數(比如數字)

示例:

int a = 10;
int &b = a;          //引用,且必須初始化
int &b = 10;         //×,錯誤,引用必須引一塊合法的記憶體,10是自變數,既不是棧區也不是堆區
const int &b = 10;   //√,正確,加上const後,編譯器會開闢出一塊臨時記憶體,int temp = 10,const int &b = temp;

引用做函數參數

作用:可以讓形參修飾實參,代替指針中形參修改實參的操作

示例:

void myswap(int &x, int &y)  //引用就是取別名,所以參數就是實參,所以可以改變實參
{
  int temp = x;
  x = y;
  y = temp;
}
int main
{
  int a = 10;
  int b = 20;
  myswap(a , b);   //引用傳遞,形參可以修改實參
  system("pause");
}

引用做函數返回值

作用:可以作為函數返回值類型返回

註意事項:不要返回局部變數的引用,函數執行完局部變數記憶體就被釋放了,返回個錘子

示例:

int& test()            //函數返回值類型就是引用類型
{
  static int a = 10;   //加個關鍵字static,這樣變數a就不是關鍵字了
  return a;
}
int main()
{
  int &ref = test();
}

引用的本質

本質:引用的本質在C++內部實現,它就是一個指針常量,由編譯器內部轉換

作用:也就說明為什麼引用初始化之後就不可更改,因為指針指向不可改

& <—等於—> int* const

示例:

int& ref = a;  <==>  int* const ref = &a
ref = 20;      <==>  *ref = 20

常量引用

作用:主要用來修飾形參,防止誤操作

用法:在函數形參列表中,加const修飾形參,防止形參改變實參

示例:

void temp(const int& val)
{
}

函數進階用法

函數的預設參數

在C++中,函數的形參列表中形參是可以用預設參數的

語法:

返回值類型 函數名 (參數 = 預設值)

{

}

註意事項:

  • 如果函數某個參數有預設值,那麼從這個位置之後的參數必須有預設值

  • 如果調用的時候有實參,那就用實參,沒有實參,就用預設值

  • 如果函數聲明有預設值,那麼在函數定義的時候就不能有預設值

示例:

int func1(int a, int b=10, int c=20)   //往後如果還有參數,必須要有預設值

int func2(int a=10, int b=20)         //函數聲明有預設值了
int func2(int a, int b)               //函數實現就不能有預設值了
{
}

函數的占位參數

作用:用來給函數的參數列表中做占位,調用函數的時候填補該位置就行了

語法:

返回值類型 函數名(數據類型)

{

}

缺點:現階段函數的占位函數存在意義不大。

示例:

void func(int a, int)     //int 就是占位參數了,只需要寫一個數據類型即可
{
}
int main
{
  func(10,20);          //調用的時候占位函數要補上
}

函數重載

作用:函數名相同,其他的可以不同,可以提高函數的復用性

滿足條件:

  • 同一個作用域下

  • 函數名稱相同

  • 函數參數類型不同,或者個數不同,或者順序不同

註意事項:

  • 函數的返回值不能作為函數重載的滿足條件

  • 具體調用的是哪一個函數,就看參數,看實參是否對應形參,比如類型、個數、順序

示例:

void func()                       //func是函數重載,這是在全局作用域下
{
}
void func(int a)                 //參數類型不同,這是在全局作用域下
{
}
void func(double a,double b)    //參數個數不同,這是在全局作用域下
{
}
void func(double a, int b)       //參數順序不同,這是在全局作用域下
{
}

引用作為函數重載

當引用作為函數參數時:

  • 實參必須是一塊合法的記憶體

  • 如果實參不是記憶體,只是一個自變數,那麼形參就必須加const來修飾

示例:

void func(int &a)            
{
}
void func(const int &a)        //這兩個func是函數重載
{
}
int main()
{
   int a = 10;
   func(a);     //調用的是第一個func函數,因為a是變數,是一塊合法記憶體
   func(10);    //調用的是第二個func函數,因為10是自變數,加const修飾本質上是申請一塊臨時記憶體存放數據10
}

遇到預設參數

  • 當函數重載遇到預設參數時,會出現二義性,也就是出錯

  • 使用時儘量避免出現預設參數

示例:

void func(int a)
{
}
void func(int a, int b = 10)
{
}
int main()
{
    func(10);    //❌,編譯器懵了,不知道該調用哪一個func
}

類與對象

  • C++本身就是面向對象的編程語言

  • 面向對象三大特性:封裝、繼承、多態

  • C++中萬物皆可為對象,對象上有屬性和行為

  • 具有相同性質的對象,稱之為類

示例:

人可以作為對象,屬性有姓名、年齡、身高···,行為有唱,跳、rap···

你和你的死黨,是同一性質,屬於人類;

車可以作為對象,屬性有輪胎、車燈、方向盤···,行為有載人、音樂、顯擺···

五菱與奧迪,是同一性質,屬於車類;

封裝

封裝的意義

封裝是C++面向對象三大特性之一

封裝的意義:

  • 將屬性和行為作為一個整體,來表現生活中的事物

  • 將屬性和行為用許可權加以控制

封裝的術語:

  • 類中的屬性和行為,統稱為成員

  • 屬性也叫成員屬性或者成員變數

  • 行為也叫成員函數或者成員方法

封裝意義一:將屬性和行為作為一個整體

語法:

class 類名{ 訪問許可權:屬性/行為 }

示例:創建一個類為圓,那半徑就是它的屬性了。

class Circle
{
public:                    //設置訪問許可權,公共許可權 
  double m_r;             //屬性——半徑,
  double calculate()      //行為——計算周長
  { 
     return 2 * PI * m_r;
  }
}
int main()
{
    Circle C1;           //通過一個類創建一個對象,對象就是圓,也就是實例化
    C1.m_r = 10;
    cout << "圓的周長:" << C1.calculate() << endl;
}

封裝意義二:將屬性和行為用訪問許可權來加以管理

訪問許可權有三種:

  • public:公共許可權,成員類內和類外都可以訪問

  • protected:保護許可權,成員類內可以訪問,,類外不可以訪問

  • private:私有許可權,成員類內可以訪問,類外不可以訪問

protecred保護許可權和private私有許可權的區別在於後面要說到的繼承上,前者子類可以訪問父類,後者子類不可訪問父類,這是後面的內容了。

示例:

class person
{
public:
  string m_name;    //姓名,類內類外都可以訪問
protected:
  string  m_car;   //汽車,類內可以訪問,類外不可以,家裡的汽車只有家裡人能用,外人不可以
private:
  int m_password;  //銀行卡密碼,類內極度私密,只有當事人能用,其他任何人甚至兒子也不能用

public:
  void func()
  {
      m_name = "張三";      //類內可以訪問
      m_car = "大眾";       //類內可以訪問
      m_password = 123456;  //類內可以訪問
  }
}

int main()
{
  person c1;
   c1.m_name = "李四";       //√,類外可以訪問
   c1.m_car = "吉利";        //×,類外不可以訪問
   c1.m_password = 456789;   //×,類外不可以訪問   
   c1.func();                //√,類外是可以訪問的
}

struct和class

struct和class都可以表示一個類,區別在於兩者預設的許可權不同:

  • struct:預設許可權為公共

  • class:預設許可權為私有

成員屬性設置為私有

優點:

  • 所有成員設置成私有,自己可以控制讀寫許可權

  • 對於寫許可權,可以檢查其數據的有效性

示例:所有成員設置成私有,自己可以控制讀寫許可權

class person
{
private:
  string m_name;    //成員設置成私有許可權,一般通過成員函數進行訪問
  int m_age;
  string m_lover;

public:
  void SetName(string name)    //這樣name就被設置成了可讀可寫的
  {
    m_name = name;
  }
  int getage()                  //age就被設置成了只讀
  {
   m_age = 18;
   return m_age;
  }
}
int main()
{
  person c1;
  c1.m_name = "張三";                   //×,因為成員是私有許可權,沒法訪問
  c1.SetName("張三");                   //√,設置姓名
  cout << c1.getage() << endl;          //√,獲取年齡
}

示例:對於寫許可權,可以檢查其數據的有效性

class person
{
private:
  string m_name;
  int age;

public:
  int GetAge(int age)    //獲取年齡,設置成可讀可寫,如果想修改年齡,範圍必須是0—100
  {
     if( age < 0 || age >100 )   //加了判斷,可以判斷數據是否有效
     {
       cout << "年齡錯了"<< endl;
       return ;
     }
     m_age = 0;
  }
}
對象的初始化和清理

C++中,初始化和清理是非常重要的安全問題:

  • 構造函數:在創建對象時為對象成員屬性初始化,該函數由編譯器自動調用

  • 析構函數:在對象銷毀前系統自動調用,執行一些清理工作

完成對象的初始化和清理工作是編譯器必須要我們做的事情,這兩個函數由編譯器自動調用,但是如果我們不寫構造函數和析構函數,編譯器就會自己去實現,只不過函數裡面是空的,簡稱空實現

構造函數

語法:

類名(){}

特點:

  • 不用寫void,沒有返回值

  • 函數名與類名相同

  • 可以有參數,因為可以發生函數重載

  • 在調用對象時編譯器自動調用,而且只會調用一次

析構函數

語法:

~類名(){}

特點:

  • 不用寫void,沒有返回值

  • 函數名與類名相同,且在前面加~

  • 不可以有參數,因此不可以發生函數重載

  • 在對象銷毀前編譯器自動調用,而且只調用一次

示例:

class Person
{
public:
   Person()       //這就是構造函數,如果不寫,編譯器會自己寫一個
  {
  }

    ~Person()     //這就是析構函數,如果不寫,編譯器會自己寫一個
  {
  }
}

構造函數的分類和調用

兩種分類方式:

  • 按參數分:有參構造和無參構造(預設構造)

  • 按類型分:普通構造和拷貝構造

三種調用方式:

  • 括弧法

  • 顯示法

  • 隱式轉換法

示例:

class Peoson()
{
public:
  Person()                         //無參構造,也就是普通構造
  {   
  }
  Person(int a)                    //有參構造
  {
  }
  Person(const Person &p)          //拷貝構造
  {
  }
}
int main()
{
//1、括弧法調用
  Person P1;                   //普通調用,不用加(),不然編譯器會誤以為是函數聲明
  Person P2(10);               //調用的是對應的有參構造
  Person P3(P1);               //調用的是對用的拷貝構造     
 
//2、顯示法調用
  Person P1;
  Person P2 = Person(10);     //調用的是對應的有參構造
  Person P3 = Person(P2);     //調用的是對應的拷貝構造

//3、隱式轉換法
  Person P1 = 10;            //調用的是有參構造,相當於Person P1 = Person(10)
  Person P2 = P1;            //調用的是拷貝構造,相當於Person P2 = Person(P1)
}

說說拷貝構造函數

所謂拷貝構造函數就是將一個創建完畢對象的屬性預設賦值給新的對象,這個賦值過程由編譯器自動完成

通常以下三種情況會調用到拷貝構造:

  • 使用一個已經創建完畢的對象來初始化一個新對象

  • 值傳遞的方式給函數參數傳參

  • 以值傳遞的方式返回局部對象

示例:

class Person()
{
  Person()
  {
  }
  Person(const Person &p)
 {
 }
}
void func1(Person p)
{
}
Person func2()
{
  Person a;
  return a;
}
int main()
{
  Person P1;
  Person P2(P1);             //使用一個已經創建好的對象來初始化一個新對象

  Person P3;
  func1(P3);                //以值傳遞的方式給函數傳參

  Person P4 = func2();      //以值傳遞的方式返回對象,它會創建一個新的對象來接住返回對象
}

構造函數的調用規則

(1)預設情況下,創建一個類編譯器至少會添加3個函數

  • 預設構造函數(無參,函數體為空)

  • 預設析構函數(無參,函數體為空)

  • 預設拷貝函數,對屬性進行預設拷貝

(2)如果開發者寫了有參構造函數,編譯器不再預設提供無參構造,但是會提供預設拷貝構造

(3)如果開發者寫了拷貝構造函數,編譯器不再提供其他構造函數,無參和有參都沒有

淺拷貝和深拷貝

這是經典面試題經常出現的案例,是一個常見的坑

淺拷貝:就是簡單的賦值拷貝

深拷貝:在堆區重新開闢一片記憶體,進行拷貝操作

註意事項:

  • 如果涉及到空間開闢和釋放,淺拷貝容易發生記憶體重覆釋放的非法操作

  • 需要自己寫一個拷貝構造函數,利用深拷貝去解決淺拷貝帶來的記憶體釋放問題

class person
{
public:
  person(){}
  person(int age,int height)
 {
    m_age = age;                        //這就是淺拷貝,就是簡單的賦值操作
    m_height = new int(height);         //這就是深拷貝,在堆區開闢一個新的記憶體
 }
person(const person &p)
{
  m_age = p.m_age;
  //m_height = p.m_height            //這是編譯器預設實現的,但是我們不要這樣的淺拷貝,會崩
  m_height = new int(*p.m_height);   //深拷貝,這樣不同的對象就有不同的堆區地址,釋放就不會發生重覆了
}

 ~person()
 {
   if(m_height != NULL)
   {
     delete m_height;      //在構造函數手動開闢了堆區,就需要手動釋放
     m_height NULL;     
   }
 }
  
private:
  int m_age;
  int *height;
}
int main()
{
  person P1(18,160);    

  person P2(P1);        //將對象P1的屬性通過淺拷貝(拷貝構造函數)copy了一份賦值給對象P2了
                        //同時淺拷貝的還包括有參構造中開闢出來的堆區地址
                        //這時候如果沒有自己寫一個拷貝構造函數的話,P1和P2就擁有一樣的堆地址
                        //結束的時候P1和P2釋放就釋放了相同的堆地址,重覆釋放屬於非法操作,程式會崩
                        //所以需要在對象中自己寫一個拷貝函數,創建一個堆區,讓不同對象有不同的堆地址
                        //這就是利用深拷貝解決淺拷貝帶來的記憶體釋放問題
}

初始化列表

作用:用來初始化屬性,一般是在構造函數中初始化

語法:

構造函數():屬性(初值),屬性(初值),屬性(初值) { }

示例:

classs person
{
public:
/*  
  person(int a,int b,int c)           //這是傳統賦值方式
  {
   m_A = a;
   m_B = b;
   m_C = c;
  }
*/
/*
  person():m_A(10),m_B(20),m_C(30)    //初始化列表的方式賦初值
 {                                    //不過這樣還是有點不夠靈活
 }
*/
  
person(int a,int b,int c):m_A(a),m_B(b),m_C(c)  //初始化列表的方式賦初值
{                                               //比上一個靈活一點
}                                               //不過跟傳統方式相比,好處就是逼格高一點而已
 

private:
  int m_A;
  int m_B;
  int m_C;
}
int main()
{
 // person p1(10,20,30);       //傳統賦初值的方式
    
 // person P1;                   //利用初始化列表,創建的同時初始化完成      
 
    person P1(30,20,10);        //初始化列表賦初值,比上一個靈活一點
                                //不過感覺跟傳統方式差不多吧,也就逼格高一點
}

類對象作為類成員

類中的成員可以是一個其他類的對象,該成員就是對象成員

註意事項:

  • 創建此類對象的時候,先構造對象成員,再構造自身

  • 先析構自身,再析構對象成員

示例:

class Phone                     //手機類
{
public:
  Phone(string pName):m_phonename(pName)
 {
 }
private:
  string m_phonename;
}
class Person                  //人類
{
public:
   //第二個參數相當於:Phone m_phone = pName(隱式轉換法,編譯器隱藏轉換)
  Person(string name,string pName):m_name(name),m_phone(pName)
 {
 }

private:
  string m_name;
  Phone m_phone;              //先創建了Phone類,再有人類
}
int main()
{
 Person p("張三","諾基亞");  //在賦值給了人類的同時,也賦值給了手機類
}

靜態成員

在成員變數和成員函數前面加一個關鍵字:static,就成了靜態成員

分類:

靜態成員變數:

  • 所有對象共用一份同一份數據

  • 在編譯階段分配記憶體

  • 類內聲明,類外初始化

靜態成員函數:

  • 所有對象共用同一個函數

  • 靜態成員函數只能訪問靜態成員變數

根據上面三個特點,靜態成員(變數或者函數)不屬於某一個對象,所以:

  • public許可權的既可以通過對象進行訪問,也可以通過類名進行訪問

  • private無論如何類外訪問不了

示例:

	   

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

-Advertisement-
Play Games
更多相關文章
  • 相信閱讀過上期文章,動手能力強的朋友們已經自己跑出來界面了。所以這期我要講的是交互部分,也就是對於滑鼠點擊事件的響應,包括計時計數對點擊事件以及一些狀態量的影響。 回憶下第一期介紹的掃雷規則和操作,游戲從開局到結束可能會涉及到哪些情況呢?我認為比較重要的就是明確什麼情況下游戲已經結束,結束代表的是勝 ...
  • # python multiprocessing庫使用記錄 需求是想並行調用形式化分析工具proverif,同時發起對多個query的分析(378個)。實驗室有40核心80線程的伺服器(雙cpu,至強gold 5218R*2)。 觀察到單個命令在分析時記憶體占用不大,且只使用單核心執行,因此考慮同時調 ...
  • 首先聲明,我不是小黑子,我不是小黑子! 作為一個ikun,時刻都在想著我們家姐姐! 這不上次用Python做了一個ikun飛機大戰,今天再給大家整活一手,Python tkinter開發一個專屬ikun音樂播放器,這樣就能時刻看到姐姐了。 咱們來看看效果 代碼實現 今天要做的就是上面的簡易音樂播放器 ...
  • # 一、編譯和鏈接的過程 ## 1、GCC生成可執行文件的總體過程 在日常的開發過程中,IDE總是會幫我們將編譯和鏈接合併,一鍵式的執行,即使在liunx中,使用命令行來編譯一個源文件也只是簡單的一句"gcc hello.c"。我們並沒有過多的關註編譯和鏈接的運行機制和機理,我想從本質出發,深入瞭解 ...
  • 本文博主給大家講解一道網上非常經典的多線程面試題目。關於三個線程如何交替列印ABC迴圈100次的問題。 > 下文實現代碼都基於Java代碼在單個JVM內實現。 ## 問題描述 給定三個線程,分別命名為A、B、C,要求這三個線程按照順序交替列印ABC,每個字母列印100次,最終輸出結果為: ``` A ...
  • # 背景 業務開發需要判斷業務狀態是否在30、40、50、60的集合內,所以寫了以下代碼 ``` int[] inLiq = {30,40,50,60}; return Arrays.asList(inLiq).contains(o.getOrderStatus()); ``` 自我Review代碼 ...
  • # Java 對象創建流程、this 關鍵字 # 1. Java 對象記憶體創建流程 > ## 1. 先載入類信息(.class 的文件),只會載入一次 > > ## 2. 在堆空間里分配對象的記憶體空間 > > ## 3.1 進行預設的初始化即數據類型本身的預設值 > > ## 3.2 進行顯式的初始 ...
  • 這幾年搞了不少靜態站點,有的是Hexo的,有的是VuePress的。由於不同的主題對於NodeJS的版本要求不同,所以本機上不少NodeJS的版本。 關於如何管理多個NodeJS版本,很早之前就寫過用nvm來管理的相關文章,這裡就不贅述了,有需要的可以看這篇[Node.js環境搭建](https:/ ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...