C++基礎-類與對象(1)

来源:https://www.cnblogs.com/wht-de-bk/archive/2022/04/20/16171665.html
-Advertisement-
Play Games

C++類與對象(1) 類的設計:可以把屬性和行為放在不同的許可權下 struct和class區別在於某人的訪問許可權不同 struct:預設共有 class:預設私有 對象的初始化和清理 如果我們不寫,系統會自己給我沒寫 構造函數的語法 類名(){} 沒有返回值,也不寫void 函數名和類型相同 可以有 ...


C++類與對象(1)

類的設計:可以把屬性和行為放在不同的許可權下

struct和class區別在於某人的訪問許可權不同

  • struct:預設共有
  • class:預設私有

對象的初始化和清理

如果我們不寫,系統會自己給我沒寫

  • 構造函數的語法 類名(){}

沒有返回值,也不寫void

函數名和類型相同

可以有參,也可以無參

在調用對象會自動調用函數,無需手動調用,只調用一次

  • 析構函數語法 ~類名(){}

同上,不過是無參(不可以重載),銷毀時自動調用函數,只調用一次

構造函數的分類與調用

分類

  • 有參和無參(預設)
  • 普通和拷貝

拷貝函數

函數名(const 函數名 &p(對象)){
    age = p.age;
}
#include<iostream>           //構造函數和析構函數
using namespace std;
class person{
public:
	int age;
public:
	//構造函數
	//普通
	person(){
		cout << "Person無參構造函數的調用" << endl;
	}
	person(int a){
		age = a;
		cout << "Person有參構造函數的調用" << endl;
	}
	//拷貝
	person(const person &p){
		age = p.age;
		cout << "Person拷貝構造函數的調用" << endl;
	}
	//析構函數
	~person(){
		cout << "Person析構函數的調用" << endl;
	}
};

void test01(){
	//調用
	//1.括弧法
	//person p1;
	//person p2(10);
	//person p3(p2);
	//註:調用預設構造函數時,不要加()(系統會以為是一個函數的聲明)
	
	//cout << "p2的年齡:" << p2.age << endl;
	//cout << "p3的年齡:" << p3.age << endl;
	
	//2.顯示法
	person p4;
	person p5 = person(10);//右值:匿名對象,當前行結束,系統收回匿名對象
	person p6 = person(p5);
	//註:不要利用拷貝函數初始化匿名對象
	
	
	//3.隱式轉換法
	person p7 = 10;//相當於person p5 = person(10);
	person p8 = p7;
}

int main(){
	test01();
 return 0;
}*/

拷貝函構造函數的調用時機

c++中調用拷貝函數一般三種請況

  • 使用一個已經創建完畢的對象來初始化一個對象
  • 值傳遞的方法給函數參數傳值
  • 以值方式返回局部對象
#include<iostream>          //拷貝時機
using namespace std;

class Person{
private:
	int m_Age;	
public:	
	Person(){
		cout << "person的預設無參構造函數調用" << endl;
	}
	
	Person(int age){
		cout << "Person有參構造函數調用" << endl;
		m_Age = age;	
	}
	
	Person(const Person &p){
		cout << "Person的拷貝調用" << endl;
		m_Age =p.m_Age;	
	}
	
	~Person(){
		cout << "person的析構函數調用" << endl;
	}	
};

void test01(){
	Person p1 = Person(20);
	Person p2(p1);	
}

void dowork(Person p){	}
void test02(){
	Person p;
	dowork(p);	
}

Person dowork2(){
	Person p1;
	cout << (int*)&p1 << endl;  //輸出地址
	return p1;
}
void test03(){
	Person p = dowork2();
	cout << (int*)&p << endl;	
}

int main(){
	test01();
    cout << endl;
	test02();
    cout << endl;
	test03();
 return 0;
}

構造函數的調用規則

預設情況下,C++會至少給一個類添加3個函數

  • 預設構造函數無參,函數體為空
  • 預設析構函數無參,函數體為空
  • 預設拷貝構造函數,對屬性進行拷貝(所有的屬性都進行賦值操作)

規則如下:

  • 如果用戶定義了有參構造函數,C++不在提供預設無參構造,但會提供預設的拷貝
  • 如果用戶定義了拷貝函數,C++不h會提供其他構造函數

註意:若只定義了拷貝(只有參同理),則

Person p1;
//和
Person p1(20);
//均是錯誤的(因為系統不會提供)

深拷貝和淺拷貝

淺拷貝:簡單的賦值拷貝操作(如果對其進行釋放,則堆區的記憶體會重覆釋放,出現錯誤)

深拷貝:在堆區重新申請空間,進行拷貝操作

m_height = new int(*P.m_height); //new返回的值是地址

private:
int * m_height;  //地址(指針)類型

初始化列表

作用:用來初始化屬性

語法:

構造函數():屬性1(值1),屬性2(值2)...{}

來個淺例吧

#include <iostream>
using namespace std;

class Person{	
public:
	//傳統初始化
//	Person(int a,int b,int c){
//		m_A = a;
//		m_B = b;
//		m_C = c;
//	}

    //初始化列表
	Person(int a,int b,int c):m_A(a),m_B(b),m_C(c){} //參數a,b,c可以自由的改變所賦的值
	
    int m_A;
    int m_B;
    int m_C;
};

void test01(){
	Person p(30,20,10);
	//Person p;
	cout << "m_A:" << p.m_A << endl;
	cout << "m_B:" << p.m_B << endl;
	cout << "m_C:" << p.m_C << endl;
}

int main(int argc, char** argv) {
	test01();	
 return 0;
}

結果是30 20 10

類對象作為類的成員

C++類中的成員可以是另一個類的對象,我們成該成員為對象成員

例如:

class A{};
class B{
    A a; 
}
//敲黑板(好老的梗...)
//先構造A的對象(即先構造其他類的對象),再構造B的對象
//析構的順序是相反的,先析構本類,再析構其他類

代碼的簡單例子

#include <iostream>
using namespace std;
#include<cstring>  //要用字元串呢

class Phone{ //類一
public:
	//品牌名字
	string m_Pname;
	
	Phone(string name){
		m_Pname = name;
	}
};

class Person{ //類二
public:
	//姓名
	string m_Name;
	//手機
	Phone m_Phone;  //類一作為類二的成員
	
	Person(string Name,string Pname):m_Name(Name),m_Phone(Pname){}
		                                      //相當於Phone m_Phone = Pname = Phone(Pname)(隱式轉化)
};

void test01(){
	Person p("張三","華為");
	cout << p.m_Phone.m_Pname;
}

int main(int argc, char** argv) {
	test01();	
 return 0;
}

結果:華為

靜態成員函數

靜態成員變數就是加上const

靜態成員分成:

  1. 靜態成員變數
    • 所有對象共用一份數據
    • 再編譯階段分配記憶體
    • 類內聲明,類外初始化
  2. 靜態成員函數
    • 所有對象共用一個函數
    • 靜態成員函數只能訪問靜態成員變數(函數體內無法區分普通變數是那個對象的成員)
    • 也是有訪問許可權的,private下在類外就訪問不到
#include <iostream>
using namespace std;
class Person{
public:
	//靜態成員函數
	void static func(){
		m_a = 100;//靜態成員函數訪問靜態成員變數
		cout << "func的調用" << m_a << endl;
	}
	static int m_a;//類內聲明類外初始化
};
int Person::m_a = 0;

//兩種訪問方式
void test01(){
    
	//通過對象訪問
	Person p;
	p.func();
	
	//通過類名訪問
	Person::func();
}

int main(int argc, char** argv) {
	test01();	
	return 0;
} 

結果

對象模型和this指針

成員變數和成員函數分開存儲

  • 只有非靜態成員變數才屬於類的對象上面

  • 空對象占用一個位元組

C++編譯器會給每個空對象分配一個位元組的空間(獨一無二的記憶體地址),防止區分空對象占記憶體的位置

#include <iostream>
using namespace std;
class Person1{};
class Person2{
    int m_a;//非靜態成員變數,屬於類的對象上
	static int m_b;//靜態成員變數,不屬於類的對象上
	void test01(){}//非靜態成員函數,不屬於類的對象上
	static void test02(){}//靜態成員函數,不屬於類的對象上  
};
int Person2::m_b = 0;

void test01(){//空對象所占用的記憶體
	Person1 p1;
	cout << "p1 sizeof of p is " << sizeof(p1) << endl;	
}

void test02(){//非空對象占用的記憶體
	Person2 p2;
	cout << "p2 sizeof of p is " << sizeof(p2) << endl;	
}
	
int main(int argc, char** argv) {
	test01();
	test02();
	return 0;
} 

this指針

引子:在上面我們知道,非靜態的成員函數只會生成一份函數實例,也是是說多個同類的對象會公用一塊代碼(一個函數),那麼:這一塊代碼是如何區分是那個對象調用自己呢?

通過this指針來解決上面的問題,this指針指向被調用的成員函數所屬的對象(eg:p1調用就指向p1...)

  • this指針是隱含在每一個非靜態成員函數內的一種指針,不用定義,直接使用

用途

  1. 當形參和成員變數重名時,可用this來區分
  2. 在類的非靜態成員函數返回對象本身(return *this;)
#include <iostream>
using namespace std;
class Person1{//名稱衝突
public:
	Person1(int age,int age1){
		this->age = age;
		age1 = age1;
	}
    
	int age;
	int age1;
	
	Person1 & Add(Person1 &p){
		this->age += p.age;
		//this是一個指向p3的指針,*this就是對象p3的本體
		return *this;	
	}
};

void test01(){
	Person1 p1(18,18);
	cout << "p1的年齡是" << p1.age << endl;
	cout << "p1的年齡是" << p1.age1 << endl;	
}
void test02(){//把p2的年齡加到p3上
	Person1 p2(10,10);
	Person1 p3(10,10);
	p3.Add(p2).Add(p2);//鏈式編程思想
	cout << "p3的年齡是" << p3.age << endl;
}

int main(int argc, char** argv) {
	test01();
	test02();
	return 0;
} 

結果

空指針訪問成員函數

C++中允許空指針調用成員函數的,但是也要註意有沒有用到this地址

如果用到this指針,則需要加以判斷確保代碼的健壯性

if(this == NULL) return;

看個小例子吧

#include <iostream>
using namespace std;
class Person{
public:
	void show(){
		cout << "show的調用" << endl;
	}
	
	int m_age;
	void get(){
		if(this == NULL) return;
		cout << "age=" << m_age << endl;
		              //預設this->m_age
	}
};


void test01(){
	Person * p = NULL;
	//空指針可以訪問成員
    p->show();
	p->get();	
}
	
int main(int argc, char** argv) {
	test01();
	return 0;
} 

const修飾成員函數

常函數

  • 不可以修改成員屬性
  • 成員屬性聲明時+mutable關鍵字,在常函數中就可以修改了
class Person{
public:
	//this本質 指針常量 Person * const this 指向不可以改變
	//在成員函數後面+const <=>const Person * const this,讓指針指向的值不可以改變
	void show() const{ 
		m_a =100;//所以會報錯哦
		//其實是this->m_a = 100;
	}
	int m_a;
	
};

常對象

  • 常對象只能調用常函數
const Person p;
p.show();

友元

在程式里,有些私有的屬性想讓類外的特殊的一些函數或者類進行調用,就需要友元技術

作用(目的):讓一個函數或是類訪問另一個類中的私有成員

友元關鍵字:friend(友元:不是類的成員,不受訪問限制)

友元的三種實現

  • 全局函數友元
  • 類做友元
  • 成員函數做友元

全局函數做友元

#include <iostream>
using namespace std;
#include<cstring>
class Building{
	//goodFriend是Building類的好朋友,可以訪問啦
	friend void goodFriend(Building &building);
    
public:
	Building(){
		SittingRoom = "客廳";
		BedRoom = "卧室";
	}
public:
	string SittingRoom;//客廳	
private:
	string BedRoom;//卧室	
};

//全局函數
void goodFriend(Building &building){
	cout << "友元全局函數 正在訪問:" << building.SittingRoom << endl;
	cout << "友元全局函數 正在訪問:" << building.BedRoom << endl;
}

void test01(){
	Building building;
	goodFriend(building);
}
	
int main(int argc, char** argv) {
	test01();
	return 0;
} 


結果

類做友元

大致流程:

  • 先創建GoodFriend類的對象GF
  • 調用本類下的構造函數:創建一個Building(同時調用Building的構造函數)
  • 訪問visit()函數,就可以訪問building下的成員啦
#include <iostream>
using namespace std;
#include<cstring>

class Building{
	//GoodFriend類是Building類的好朋友
	friend class GoodFriend;
    
...//和上面一樣
};

class GoodFriend{
public:
	GoodFriend(){
		//創建對象
		building = new Building;
	}
    
	void visit(){//參觀函數 訪問Building中的屬性
		cout << "友元正在訪問:" << building->SittingRoom << endl;
		cout << "友元正在訪問:" << building->BedRoom << endl;
	}
    
	Building * building;
};

void test01(){
	GoodFriend GF;
	GF.visit();
}
	
int main(int argc, char** argv) {
	test01();
	return 0;
} 


結果

成員函數做友元

流程與上面的幾乎一樣

#include <iostream>
using namespace std;
#include<cstring>

class Building;//防止在未創建BUilding類是報錯
class GoodFriend{
public:
    Building * building;
	GoodFriend();
	void visit();//參觀函數 訪問Building中的私有成員
};

class Building{
    //visit()做為BUilding類的好朋友
	friend void GoodFriend::visit();
    
public:
	string SittingRoom;//客廳
private:
	string BedRoom;//卧室
    
public:
	Building();
};

//類外聲明
Building::Building(){
	SittingRoom = "客廳";
	BedRoom = "卧室";
}

GoodFriend::GoodFriend(){
	building = new Building;
}
void GoodFriend::visit(){//參觀函數 訪問Building中的私有成員
	cout << "友元正在訪問:" << building->SittingRoom << endl;
	cout << "友元正在訪問:" << building->BedRoom << endl;
}

void test01(){ //測試函數
	GoodFriend GF;
	GF.visit();	
}
	
int main(int argc, char** argv) {
	test01();
	return 0;
} 


結果:

運算符的重載

概念:對已有運算符重新進行定義,賦予其另一種功能,以適應不同的數據類型

對於內置的數據類型,系統知道如何進行運算

加號運算符重載(其他同理)

  1. 成員函數重載+號

本質:Person p3 = p1.operator+(p2);

#include <iostream>
using namespace std;
class Person{
public:
	int m_A;
	int m_B;
/*======================================================*/
	Person operator+(Person &p){
		Person temp;
		temp.m_A = this->m_A + p.m_A;
		temp.m_B = this->m_B + p.m_B;
		return temp;
	}
/*======================================================*/
};

void test01(){
	Person p1;
	p1.m_A = 10;
	p1.m_B = 10;
	Person p2;
	p2.m_A = 10;
	p2.m_B = 10;
	
	Person p3 = p1 + p2;
	
	cout << p3.m_A <<endl;
	cout << p3.m_B <<endl;	
}

int main(int argc, char** argv) {
	test01();	
 return 0;
}

結果是兩個20(相加成功)

  1. 全局函數重載+號

本質:Person p3 = operator+(p1,p2);

#include <iostream>
using namespace std;
class Person{
public:
	int m_A;
	int m_B;	
};
/*======================================================*/
Person operator+(Person &p1,Person &p2){
	Person temp;
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
	return temp;
}
/*======================================================*/
int main(int argc, char** argv) { //函數和上面的一樣
	test01();	
 return 0;
}

結果也是兩個20

註意:- 運算符的重載也可以發生函數重載(名字相同,參數不同)

       - 不可以改變內置運算符

左移運算符的重載(<<)

只能利用全局函數重載左移運算符

作用:輸出自定義的數據類型

本質:operator<<(cout,p) => cout << p

#include <iostream>
using namespace std;
class Person{
public:
	int m_A;
	int m_B;
};
/*======================================================*/
ostream & operator<<(ostream &out,Person &p){
	out << "m_A:" << p.m_A << '\t' << "m_B:" << p.m_B << endl;
	return out;
}
/*======================================================*/
void test01(){
	Person p;
	p.m_A = 10;
	p.m_B = 10;
	cout << p << endl;
}

int main(int argc, char** argv) {
	test01();	
 return 0;
}

結果:m_A:10 m_B:10

類的成員變成私有:用友元

class Person{
     friend ostream & operator<<(ostream &out,Person &p);
public:
	int m_A;
	int m_B;
};

遞增運算符重載(++)

前置返回引用,後置返回值

  • 前置遞增
 #include <iostream>
 using namespace std;
 class MyInteger{//自定義的整型
   friend ostream & operator<<(ostream &out,MyInteger &p);
 public:
   MyInteger(){
     m_Num = 0;
   }
   /*======================================================*/
   MyInteger & operator++(){//返回引用是為了一直對一個數據操作
     m_Num++;
     return *this;
   }
   /*======================================================*/
 private:
   int m_Num;
 };

 ostream & operator<<(ostream &out,MyInteger &p){
   out << p.m_Num;
   return out;
 }

 void test01(){
   MyInteger myint;
   cout << "myint:" << ++(++myint) << endl;
   cout << "myint:" << myint << endl;
 }

 int main(int argc, char** argv) {
   test01();  
 return 0;
 } 

結果1:myint:2(換行)myint:2

  • 後置遞增
#include <iostream>
using namespace std;
class MyInteger{//自定義的整型
	friend ostream & operator<<(ostream &out,const MyInteger &p);
public:
	MyInteger(){
		m_Num = 0;
	}  
	/*======================================================*/
	MyInteger  operator++(int){//int 代表占位參數,可以用於區分前置和後置遞增
		//先記錄
		MyInteger temp = *this;
		//後遞增
		m_Num++;
		//再返回
		return temp;
	}
	/*======================================================*/
private:
	int m_Num;
};
ostream & operator<<(ostream &out, const MyInteger &p){//這裡加了const,否則在test02()的輸出會有問題
	out << p.m_Num;
	return out;
}

void test02(){
	MyInteger myint;
	cout << "myint:" << myint++ << endl;
	cout << "myint:" << myint << endl;
}

int main(int argc, char** argv) {
	test02();
	return 0;
} 

結果2:myint:0(換行)myint:1

賦值運算符重載

補充構造函數調用規則,一個類至少4個函數

  • 第四個:賦值運算符operator=,對屬性進行拷貝

p2 = p1的問題:堆區重覆釋放,和淺拷貝的問題是一樣的

#include <iostream>
using namespace std;
class Person{
public:
	int *m_age;//開闢到堆區
	
	Person(int age){
		m_age = new int(age);
	}
    
	~Person(){
		if(m_age != NULL){
			delete m_age;
			m_age = NULL;
		}	
	}
    /*======================================================*/
    Person & operator=(Person &p){//和深拷貝幾乎是一樣的,返回值是引用是要滿足連等
		//先判斷左值是否有屬性在堆區,如果有,先釋放乾凈,再深拷貝
		if(m_age != NULL){
			delete m_age;
			m_age = NULL;		
		} 
		m_age = new int(*p.m_age);
		//返回對象本身
		return *this;
	}
};
/*======================================================*/
void test01(){
	Person p1(10);
	Person p2(20);
	Person p3(30);
	p3 = p2 = p1;//賦值操作
	cout << "p1的年齡是:" << *p1.m_age << endl;
	cout << "p2的年齡是:" << *p2.m_age << endl;
	cout << "p3的年齡是:" << *p3.m_age << endl;
}

int main(int argc, char** argv) {
	test01();
	return 0;
} 


結果:p1,p2,p3都是10

關係運算符的重載(>/<...)

  • ==的重載(!=同理)
#include <iostream>
using namespace std;
class Person{
public:
	string m_name;
	int m_age;
	
	Person(string name,int age){
		m_name = name;
		m_age = age;
	}
	~Person(){}
	/*======================================================*/
	bool operator==(Person &p){
		if(this->m_name == p.m_name&&this->m_age == p.m_age){
			return true;	
		}else{
			return false;
		}
	}
    /*======================================================*/
};
void test01(){
	Person p1("Tom",18);
	Person p2("Tom",18);
	if(p1 == p2) cout << "p1和p2相等" << endl;
	else cout << "p1和p2不相等" << endl;
}
	
int main(int argc, char** argv) {
	test01();
	return 0;
} 

結果:p1和p2相等

函數調用運算符重載

  • 函數調用運算符()也可以重載
  • 由於重載後使用的方式非常像函數的調用,因此也稱謂仿函數
  • 仿函數沒有固定的寫法,很靈活
#include <iostream>//寫了兩個...
using namespace std;
#include<cstring>
class Myprint{//列印類
public:
    /*======================================================*/
	void operator()(string test){
		cout << test <<endl;
	}
    /*======================================================*/
};

class MyAdd{//加法類
	public:
    /*======================================================*/
	int operator()(int a,int b){
		return a+b;	
	}
    /*======================================================*/
};

void test01(){
	Myprint myPrint;
	myPrint("hello word");
    
	MyAdd myAdd;
	int c = myAdd(1,2);
	cout << c << endl;
    
	//匿名對象
	cout << MyAdd()(1,1) << endl;
}
	
int main(int argc, char** argv) {
	test01();
	
	return 0;
} 

結果:hello word(換行)3(換行)2


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

-Advertisement-
Play Games
更多相關文章
  • 作者:zxg_神說要有光 原文鏈接:https://juejin.cn/post/7087172219226292237 React 的 hooks 是在 fiber 之後出現的特性,所以很多人誤以為 hooks 是必須依賴 fiber 才能實現的,其實並不是,它們倆沒啥必然聯繫。 現在,不止 re ...
  • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="wi ...
  • 註釋快捷鍵——ctrl+/ 標題標簽H1-H6 H1-H6屬性:align="left/center/right" 左 中 右 段落標簽 <p></p> <p></p>屬性:align="left/center/right" 左 中 右 換行標簽<br> 分割線<hr> &nbsp:空格 一個漢字占 ...
  • 說到防抖和節流相信大家都不陌生,這兩個東西大家可能多多少少都有用到過,最少也有聽。簡單來說,防抖和節流都是用來減少函數執行的頻率,以達到優化項目性能或者實現特定功能的效果。 ...
  • 最近項目中用到可視化地圖,正好這幾天有空整理下以方便以後快速上手使用的記錄。 不廢話~ 我們項目是用的uinapp 剛開是也是npm 裝包 npm install echarts -S //或 cnpm install echarts -S 你隨意。 window.wx = undefined; 這 ...
  • 外觀(門面)模式 化零為整,把零碎的功能拼成一個整體,對外提供一個統一介面,用來訪問子系統中的多個介面。 總結 解耦,不需要一個個對接,使用簡單。 單例模式 負責創建對象,同時確保只有單個對象被創建。 餓漢式 線程安全,在類載入時就會進行初始化,訪問時直接使用。 public class Stude ...
  • 一、前言 這是《大話雲原生》系列的第二篇,第一篇《煮餃子與docker、kubernetes之間的關係》推出之後受到大家的歡迎,很多朋友聯繫到我給我加油打氣,感謝!我會繼續寫下去! 書接上回介紹了《煮餃子與docker、kubernetes之間的關係》之後,小娜同學(我老婆)問:為什麼不把服務統一開 ...
  • 列表基本上是 Python 中最常用的數據結構之一了,並且刪除操作也是經常使用的。 那到底有哪些方法可以刪除列表中的元素呢?這篇文章就來總結一下。 一共有三種方法,分別是 remove,pop 和 del,下麵來詳細說明。 remove L.remove(value) → None -- remov ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...