主頁 > 後端開發 > C語言面向物件(上):面向物件三大特性的實作

C語言面向物件(上):面向物件三大特性的實作

2021-12-21 08:04:45 後端開發

目錄

  • C語言面向物件
    • 為什么要面向物件
    • 面向物件三大特性的實作
      • 封裝
        • 檔案
        • 封裝性小結
      • 繼承
      • 多型
        • 函式指標
        • 父結構體指標
        • 弱函式
    • 總結

C語言面向物件

為什么要面向物件

C語言作為一門面向程序的高級語言,具有非常高的運行效率,但相對來說它的封裝和擴展性能就沒有那么強,為了能夠寫出具有足夠封裝性和擴展性的C語言程式,我們就需要用面向物件的思想來撰寫C語言程式,

有人可能會覺得面向物件的效率低,但事實上C語言運行已經十分高效,面向物件的編程方式并不會帶來非常顯著的效率下降,其次,當工程所涉及的模塊越來越多,功能越來越復雜的時候,面向物件就成為了必然選擇,

面向物件可以降低代碼耦合度,你是否遇到過為了加某一新功能,牽一發而動全身,引入了諸多BUG不說,最后還沒能完全支持新功能,這種情況大概率是因為函式及變數各處隨意呼叫導致代碼耦合度過高,或者函式介面設計不當,不得不持續引入新的函式介面以實作新功能,因此能否基于面向物件的思想撰寫高擴展性和可維護性的C程式越來越成為判斷一名C語言程式員水平高低的標準之一,

下面將介紹幾種簡單的C語言面向物件的方法,這些方法僅根據我個人經驗總結得出,如有謬誤歡迎指正,

面向物件三大特性的實作

面向物件的編程思想具有三大特征:封裝、多型、繼承,

封裝

按我的理解,封裝就是把具有相同性質的變數、函式及介面統一管理,只能通過某個渠道才能訪問里面的內容,好比是一個存放了各種東西的倉庫,只能用特定鑰匙才能打開它并使用倉庫里存放的東西,這個倉庫就是對里面存放東西的封裝,外面看不到里面到底有什么,以JAVA為例,JAVA的封裝性體現在類(class)、檔案(.java)和包(package)上,

類是能夠體現封裝性最重要的特征之一,JAVA中一個類的非靜態成員可以通過該類的實體物件訪問,而在C語言中,就需要結構體來承擔JAVA中類的職責,所謂類的實體物件,在C中就是以該結構體為變數型別的變數,很多時候我們會用typedef來將一個結構體定義為一個型別,型別命名時常以_t作為結尾,但與JAVA的類不同的是,訪問一個C的結構體可以通過一個變數或者是該結構體型別的指標,

typedef struct {
	int a;
	char b;
} example_t;
// 實體物件
example_t obj = {
		.a = 1, 
		.b = 'b'
		};
// 指標形式
example_t *p_obj = (example_t *)malloc(sizeof(example_t));
p_obj->a = 2;
p_obj->b = 'z';

上面只是變數的實作方法,那么有沒有辦法在結構體內包含一個函式呢?C語言中雖然不能直接將一個函式放到結構體里面,但是可以通過函式指標實作,而這個函式指標類似于是JAVA中的抽象函式,指定了函式回傳值型別以及引數串列個數及其型別,但其本質仍是一個指標,我們需要為該函式指標賦值,即將其指向同型別的函式地址

typedef struct {
	int a;
	char b;
	int (*func)(int, int);
} example_t;

int get_sum(int x, int y)
{
	return x + y;
}

void test(void)
{
	example_t obj = {
		.a = 1,
		.b = 'c',
		.func = get_sum
	};
	// 回傳值為9
	int ret = obj.func(4, 5);
}

上例中,*func表示回傳值為int,引數串列有兩個引數,都是int,符合這個特征的函式指標,我們可以看到get_sum剛好符合這個條件,我們就可以把get_sum的函式地址賦值給func,當通過obj呼叫func時,實際上會跳轉到get_sum的地址,并開始執行get_sum中的代碼,通過上例我們不難看出,只要符合*func所指定型別的,不管函式內部執行什么都可以,那么我們就也可以用不同的函式去賦值,實作不同的功能,

typedef struct {
	int a;
	char b;
	int (*func)(int, int);
} example_t;

int get_sum(int x, int y)
{
	return x + y;
}

int get_minus(int x, int y)
{
	return x - y;
}

void test(void)
{
	example_t obj = {
		.a = 1,
		.b = 'c',
		.func = get_sum
	};
	// 回傳值為9
	int ret1 = obj.func(4, 5);
	// 改變指向的函式
	obj.func = get_minus;
	// 回傳值變為-1
	int ret2 = obj.func(4, 5);
}

至此我們可以看到C語言的結構體也可以實作類似于JAVA中類的功能,甚至實作了部分多型的功能,

檔案

檔案也是封裝性的體現之一,為什么這么說呢?對于一個.c檔案,如果其他檔案想要呼叫它的全域變數或者函式等,可以通過extern關鍵字宣告后使用,如果我們希望一些變數或者函式只能夠在本檔案中使用的時候(即不希望被外界呼叫),那么可以通過static關鍵字修飾,可以理解為static關鍵字修飾的變數或函式被封裝到這個.c檔案里面了,只允許該.c檔案自己使用,這樣可以避免跨檔案隨意呼叫造成的耦合,

// 在檔案1中定義了變數a,sum和被static修飾的minus函式
int a = 1;
int sum(int a, int b)
{
	return a + b;
}
static int minus(int a, int b)
{
	reutn a - b;
}
// 在檔案2中呼叫檔案1中的a和sum函式
void test(void)
{
	// 在函式內extern表示該變數或函式的作用域僅限于該函式,可防止擴大其作用域,破壞封裝性
	extern int a;
	extern int sum(int a, int b);
	// 被static修飾的變數或函式不能extern
	// extern int minus(int a, int b);
	int res = sum(a + 5);
}

JAVA中通過import引入jar包,C語言中則是通過include頭檔案引入頭檔案中定義的函式、型別等,因此只有那些允許被其他檔案使用的函式、變數、型別、宏等才應該被放入.h頭檔案中,即意味著放入頭檔案中的內容將被公開,如果把只有內部(即.h對應的.c檔案)才需要用到的一些函式或變數等放入頭檔案,一方面會引起呼叫者的困惑,一堆亂糟糟的函式不知道那些需要被呼叫,另一方面,呼叫者可能因錯誤呼叫本不應被公開的函式等,導致內部狀態等改變,進而引起程式執行出錯,所以當我們用面向物件的思路編程時,一定要非常小心哪些內容應放到頭檔案,可以被公開,而哪些不需要,那么不需要的最好可以通過static修飾,

// algorithm.c源檔案
static int sum(int a, int b)
{
	return a + b;
}
static minus(int a, int b)
{
	return a - b;
}
static int multiple(int a, int b)
{
	return a * b;
}
int calculate(int a, int b, int c)
{
	return multiple(sum(a, b), minus(b, c));
}
// algorithm.h頭檔案
int calculate(int a, int b, int c);
// test.c源檔案
#include "algorithm.h"
void test(void)
{
	int res = calculate(2, 6, 3);
}

注意上述例子是在test.c源檔案中include,那么和.h頭檔案中include有什么區別呢?假設我們在test.h檔案中include了algorithm.h,再由test.c include test.h,我們也能實作相同的功能,但是,如果我們還有一個app.h頭檔案include了test.h,那么algorithm.h中定義的內容也會泄露到app.h中形成連鎖反應,所以如果只是源檔案用到了某個頭檔案,盡量不要用下面例子中在它自身的頭檔案中去include,而是像上面代碼一樣,在源檔案中include,否則非常容易導致回圈依賴,導致編譯不通過,比如algorithm.h又include了app.h,就會形成回圈依賴,相當于《我include了我自己》,

// algorithm.h頭檔案
#incluide "app.h"
int calculate(int a, int b, int c);
// test.h頭檔案
#include "algorithm.h"

int test(void);
// test.c源檔案
#include "test.h"
int test(void)
{
	return calculate(2, 6, 3) + 5;
}
// app.h頭檔案
/* 這里相當于同時include了"algorithm.h"
 * "algorithm.h"和"app.h"形成了回圈依賴,非常容易出錯 */
#include "test.h"

封裝性小結

JAVA中的類對應C中的結構體,private作用域相當于是加了static的函式,default的作用域類似于沒有加static但是也沒有在頭檔案中宣告的函式,其他檔案仍可通過extern參考,而頭檔案中的內容則可以認為是public的內容,

繼承

繼承即是在父類或者基類的基礎上,由子類繼承其變數、函式型別,并根據需求進行擴充,一般父類中定義的是所有子類都具有的屬性或者通用的方法,通過繼承,我們可以規范子類成員的型別的方法,但目前我并沒有遇到C語言中可以實作繼承的比較好的方式,只能將父類,即父結構體,以成員的形式放在子結構體中

// 父結構體
typedef struct {
	const char *name;
	int age;
} people_t;
// 子結構體1
typedef struct {
	people_t base;
	const char *school;
} student_t;
// 子結構體2
typedef struct {
	people_t base;
	int salary;
	int (*earn_money)(void);
} adult_t;
// 孫結構體
typedef struct {
	adult_t base;
	bool has_beard;
} man_t;

我們知道,在C語言中,結構體其實是存盤了資訊的一片空間,結構體內部的變數或者指標等,都是只是用來標識該資訊存放相對于結構體起始位置的偏移以及所占空間大小,以上面孫結構體為例,其實際結構體存盤的內容為:

typedef struct {
	const char *name;
	int age; // people_t 型別訪問邊界
	int salary;
	int (*earn_money)(void); // adult_t 型別訪問邊界
	bool has_beard; // man_t 型別訪問邊界
} man_t;

如果我們有一個man_t型別的變數x,要訪問其age成員,那么我們需要呼叫x.base.base.age,還有一種方法是將其強轉為people_t型別,并通過people_t指標訪問age成員

void test(void)
{
	man_t x;
	// 一般訪問方式
	x.base.base.age = 30;
	// 父型別指標方式
	people_t *peo = (people_t *)&x;
	peo->age = 30;
}

以此類推,當我們傳入一個函式的時候,也可以用父結構體指標的方式來擴展可接受的引數型別,相當于JAVA中可以通過父類物件接受子類物件,如

void init_people(people_t *peo)
{
	if(peo == NULL)
		return;
	peo->name = "default";
	peo->age = "20";
}

void test(void)
{
	man_t man;
	adult_t adt;
	student_t stu;
	init_people((people_t *)&man);
	init_people((people_t *)&adt);
	init_people((people_t *)&stu);
}

(ps. 其實C語言中我們甚至還可以將父類物件強轉為子類物件,不過這種方式有一定訪問非法地址的風險,所以需要有限制條件,會在后續內容中提到)

多型

所謂多型,用更加通俗一點的理解就是通過相同的介面,達到實作不同功能的目的,JAVA中多型一般體現為重寫(Override),它是基于繼承機制的一個操作,即在不同子類中根據需求重寫父類的一個介面,以實作不同功能,在C中,有三種方式可以實作多型,

函式指標

函式指標已經在介紹C語言封裝的時候提到了,函式指標規定了所指向函式的型別,即統一了函式介面,并可以通過該函式指標跳轉對應函式,

typedef struct {
	int pre_val;
	int (*func)(int a, int b);
} alg_t;

int sum(int a, int b)
{
	return a + b;
}

int munus(int a, int b)
{
	return a - b;
}

void test(void)
{
	// 實體物件alg1的func函式指標指向了sum函式
	alg_t alg1 = {
		.pre_val = 0,
		.func = sum
	};
	// 實體物件alg2的func函式指標指向了minus函式
	alg_t alg2 = {
		.pre_val = 0,
		.func = minus
	};
	// 呼叫不同物件的同一介面,實作不同功能
	alg1.pre_val = alg1.func(4, 5); // 結果為9
	alg2.pre_val = alg2.func(4, 5); // 結果為-1
}

父結構體指標

這種方式其實更加接近函式的多載,但不能改變引數個數,當一個介面中允許傳入的引數是一個父結構體指標時,事實上我們也可以傳入一個子結構體指標,

typedef enum {
	DEFAULT,
	STUDENT,
	TEACHER
} role_t;
// 父結構體
typedef struct {
	const char *name;
	int age;
	role_t role; // 用來標識子結構體型別
} people_t;
// 子結構體1
typedef struct {
	people_t base;
	const char *school;
	int score;
} student_t;
// 子結構體2
typedef struct {
	people_t base;
	const char *course;
} teacher_t;

void init_people(people_t *peo)
{
	if (peo == NULL)
		abort();
	// 判斷子結構體型別
	switch (peo->role) {
	case DEFAULT:
		peo->name = "Unkown";
		peo->age = -1;
	case STUDENT:
		// 注意這里將父結構體指標強轉為子結構體指標務必保證型別正確,否則可能會訪問非法地址及資料溢位風險
		student_t *stu = (student_t *)peo;
		stu->name = "John";
		stu->age = 10;
		stu->school = "Tsinghua"
		stu->score = 99;
		break;
	case TEACHER:
		// 注意這里將父結構體指標強轉為子結構體指標務必保證型別正確,否則可能會訪問非法地址及資料溢位風險
		teacher_t *tea = (teacher_t *)peo;
		tea->name = "Mike";
		tea->age = 35;
		tea->course= "C language";
		break;
	default:
		abort();
	}
}

void test(void)
{
	student_t stu;
	teacher_t tea;
	// 這里需要正確標識子結構體型別,才能將子結構體強轉回父結構體
	stu.base.role = STUDENT;
	tea.base.role = TEACHER;
	init_people((people_t *)&stu);
	init_people((people_t *)&tea);
}

以此類推,其實上文中的父結構體指標也可以被void *代替,這樣我們的父結構體就不一定必須要在頭檔案中定義,但此時父結構體中的內容在子結構體中必須單獨自己定義,單獨定義時因為沒有任何約束條件,所以必須同父結構體的變數型別、順序等相同,即在存盤中的存盤格式相同,

// init.h頭檔案
typedef enum {
	STUDENT,
	TEACHER
} role_t;

// 子結構體1
typedef struct {
	// 父結構體中的欄位
	const char *name;
	int age;
	role_t role; // 用來標識子結構體型別
	// 子結構體中額外的欄位
	const char *school;
	int score;
} student_t;

// 子結構體2
typedef struct {
	// 父結構體中的欄位
	const char *name;
	int age;
	role_t role; // 用來標識子結構體型別
	// 子結構體中額外的欄位
	const char *course;
} teacher_t;

void init_people(void *peo);
// init.c源檔案
#include "init.h"

// 父結構體
typedef struct {
	const char *name;
	int age;
	role_t role; // 用來標識子結構體型別
} people_t;

void init_people(void *peo)
{
	if (peo == NULL)
		abort();
	people_t people = (people_t *) peo;
	// 判斷子結構體型別
	switch (people ->role) {
	case STUDENT:
		// 注意這里將父結構體指標強轉為子結構體指標務必保證型別正確,否則可能會訪問非法地址及資料溢位風險
		student_t *stu = (student_t *)peo;
		stu->name = "John";
		stu->age = 10;
		stu->school = "Tsinghua"
		stu->score = 99;
		break;
	case TEACHER:
		// 注意這里將父結構體指標強轉為子結構體指標務必保證型別正確,否則可能會訪問非法地址及資料溢位風險
		teacher_t *tea = (teacher_t *)peo;
		tea->name = "Mike";
		tea->age = 42;
		tea->course= "C language";
		break;
	default:
		abort();
	}
}
// test.c源檔案
#include "init.h"
void test(void)
{
	student_t stu;
	teacher_t tea;
	// 這里需要正確標識子結構體型別,才能將子結構體強轉回父結構體
	stu.role = STUDENT;
	tea.role = TEACHER;
	init_people(&stu);
	init_people(&tea);
}

void *代替父結構體指標的好處是,父結構體不必被暴露出來,即上層呼叫者(這里是test.c)不會傳入一個父結構體的物件,但是缺陷是,由于傳入的指標型別是void *,資料型別不明顯,上層呼叫者有可能會傳入其他奇怪的指標,另外,不管是父結構體指標還是void *這樣做雖然增強了介面函式的通用性,但是還是會有指向非法地址或者溢位風險,比如上層呼叫者將role配錯,就有可能引起不可預料的風險,因此需要謹慎使用,

弱函式

GUN C支持弱函式,可以通過在函式名前加__attribute__((weak))來表示一個函式是弱函式,弱函式一般用來實作一個介面的默認功能,當有一個回傳值、函式名、引數串列完全相同的函式在其他地方被定義是,該弱函式會被覆寫,也就相當于JAVA中的重寫(override),但是弱函式的方式只能允許同時定義一弱一強兩個函式,不能夠重復多次使用,在下面例子中,只有當宏定義OVERRIDE_EN是非零時,強函式才會被定義,并覆寫弱函式,否則將執行弱函式,

#define OVERRIDE_EN		1
__attribute__((weak)) int calc(int a, int b)
{
	return a + b;
}
#if OVERRIDE_EN
int calc(int a, int b)
{
	return a - b;
}
#endif
void test(void)
{
	// 當OVERRIDE_EN定義為非零時,結果為-1, 當OVERRIDE_EN未定義或為0時,結果為9
	int res = calc(4, 5);
}

總結

本文簡單介紹了一些C語言實作面向物件的方法,可見C語言也能有物件(學C語言也能有物件),至于這些面向物件的方法到底有什么用?下一篇文章將會以驅動設計為例,簡單介紹一些應用的例子,(撰寫中)

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/387885.html

標籤:java

上一篇:JavaWeb--快速入門JavaScript

下一篇:jdbc增刪改查操作,封裝工具類,實作泛型介面無限套娃

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more