C++11 智能指針 shared_ptr

来源:https://www.cnblogs.com/champrin/archive/2023/01/17/17056814.html
-Advertisement-
Play Games

C++11 智能指針 shared_ptr Written on 2023-01-16 個人學習智能指針記錄合集: C++11 智能指針 C++11 智能指針 shared_ptr C++11 智能指針 unique_ptr C++11 智能指針 weak_ptr std::shared_ptr 共 ...


C++11 智能指針 shared_ptr

Written on 2023-01-16

個人學習智能指針記錄合集:

std::shared_ptr 共用智能指針,也被稱為計數智能指針。

共用智能指針會記錄有多少個共用智能指針指向同一個對象,當這個數為 0 的時候,程式自動的預設釋放(析構)這個對象,記錄有多少個的這個方法叫做引用計數

共用智能指針可以有多個共用智能指針同時管理同一個對象。

舉個慄子

普通指針管理

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

class Person{
public: 
    Person(){ cout << "Constructor: person's age = " << m_age << endl; }
    Person(int age) : m_age(age){ cout << "Constructor: person's age = " << m_age << endl; }
    ~Person(){ cout << "Destructor: person's age = " << m_age << endl; }
    void getAge(){ cout << "Person's age = " << m_age << endl; }
private:
    int m_age = 0; 
}; 

int main()
{
    {
        Person *p = new Person(18);
    }

    cout << endl << "Before return" << endl;
    return 0;
}
/** 輸出:
Constructor: person's age = 18

Before return

**/

依輸出可見,p指向的對象並沒有被析構,因為並沒有析構函數中的列印,這造成了記憶體泄漏。

shared_ptr智能指針管理

// ...
int main()
{
    {
        shared_ptr<Person> sPtr1 {new Person(18)};
    }

    cout << endl << "Before return" << endl;
    return 0;
}
/** 輸出:
Constructor: person's age = 18
Destructor: person's age = 18

Before return

**/

依輸出可見,離開了程式塊的作用域後,析構函數中的列印體現出來了,sPtr1管理的對象自動的被析構了。

獲取引用計數

long use_count() const noexcept;

shared_ptrnullptr 時,返回為 0。

初始化

創建空管理的 shared_ptr

constexpr shared_ptr() noexcept;
constexpr shared_ptr(std::nullptr_t) noexcept;
  • std::nullptr_t:空指針nullptr

創建對非數組對象和數組對象管理的 shared_ptr

template< class Y >
explicit shared_ptr( Y* ptr );
  • Y:動態分配的對象的類型
// ...
int main()
{
    shared_ptr<int> sPtr1; // 創建空管理的shared_ptr
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    
    shared_ptr<int> sPtr2(nullptr); // 創建空管理的shared_ptr
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    
    shared_ptr<int> sPtr3(new int(100)); // 創建對非數組對象管理的shared_ptr
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    
    shared_ptr<int> sPtr4(new int[10]); // 創建對數組對象管理的shared_ptr
    cout << "sPtr4 use count = " << sPtr4.use_count();
    return 0;
}
/** 輸出:
sPtr1 use count = 0
sPtr2 use count = 0
sPtr3 use count = 1
sPtr4 use count = 1
**/

解釋:
sPtr1sPtr2都為nullptr,故引用計數都為 0;
sPtr3sPtr4所管理的對象,都只有一個 shared_ptr 管理,故引用計數都為 1。

創建指定刪除器的 shared_ptr

template< class Y, class Deleter >
shared_ptr( Y* ptr, Deleter d );

template< class Deleter >
shared_ptr( std::nullptr_t ptr, Deleter d );

template< class Y, class Deleter, class Alloc >
shared_ptr( Y* ptr, Deleter d, Alloc alloc );

template< class Deleter, class Alloc >
shared_ptr( std::nullptr_t ptr, Deleter d, Alloc alloc );
  • Deleter:刪除器,在引用計數為 0 時,shared_ptr 會自動的執行刪除器
  • Alloc:分配器(Allocator)
  1. 預設刪除器:預設對指向的記憶體地址進行釋放,即析構掉這塊地址的內容
    1. 非數組類型,以 delete ptr 為預設刪除器
    2. 數組類型,以 delete[] ptr 為預設刪除器
  2. 指定刪除器:能夠自行指定引用計數為 0 時,要做什麼,比如有些場景不需要對指向的記憶體進行釋放,比如關閉指向文件,關閉指向套接字
    1. 指定刪除器的函數結構:
      void deletePtr(T* p){ ... }
      
    2. 也可使用 Lambda表達式

例,對於文件的使用場景,不是直接delete文件,而是關閉文件:

// ...
void closeFile(FILE* fp) 
{
    if (fp == nullptr) return;
    fclose(fp);
    cout << "File closed" << endl;
}
int main() 
{
    FILE* fp = fopen("data.txt" ,"w");
    shared_ptr<FILE> sfp{fp,closeFile};
    if (sfp == nullptr)
        cout << "Failed opened" << endl;
    else 
        cout << "File opened" << endl;
    return 0;
}

創建通過現有共用智能指針的 shared_ptr

主要是通過複製構造函數和移動構造函數std::move()

shared_ptr( const shared_ptr& r ) noexcept;

template< class Y >
shared_ptr( const shared_ptr<Y>& r ) noexcept;

shared_ptr( shared_ptr&& r ) noexcept;

template< class Y >
shared_ptr( shared_ptr<Y>&& r ) noexcept;    

例,

// ...
int main()
{
    shared_ptr<int> sPtr1(new int(100));
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl << endl;
    // 創建通過現有現有共用智能指針的 shared_ptr
    // 通過拷貝構造函數創建
    shared_ptr<int> sPtr2(sPtr1);
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl << endl;
    // 通過賦值運算符創建
    shared_ptr<int> sPtr3 = sPtr1;
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl << endl;
    // 通過移動構造函數創建
    shared_ptr<int> sPtr4(move(sPtr1));
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count() << endl << endl;
    std::shared_ptr<int> sPtr5 = move(sPtr2);
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count() << endl;
    cout << "sPtr5 use count = " << sPtr5.use_count();
    return 0;
}
/** 輸出:
sPtr1 use count = 1

sPtr1 use count = 2
sPtr2 use count = 2

sPtr1 use count = 3
sPtr2 use count = 3
sPtr3 use count = 3

sPtr1 use count = 0
sPtr2 use count = 3
sPtr3 use count = 3
sPtr4 use count = 3

sPtr1 use count = 0
sPtr2 use count = 0
sPtr3 use count = 3
sPtr4 use count = 3
sPtr5 use count = 3
**/

解釋:
sPtr2sPtr3都是通過對sPtr1執行了複製構造函數,因此sPtr1的引用計數一次增加 1;sPtr2sPtr3同理。
通過move(sPtr1),使得sPtr1釋放了被管理對象的所有權,此時sPtr1被設置為nullptr,因此sPtr1引用計數為 0;sPtr2同理。

通過 std::make_shared

template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
  • T:非數組,指針指向的數據類型
  • Args&&... argsT 的構造函數參數列表

這種初始化效率更高,在 C++17 之前的編譯器更安全

// ...
int main()
{
    shared_ptr<int> sPtr1 = make_shared<int>(100);
    shared_ptr<int> sPtr2(make_shared<int>(200));
    shared_ptr<int> sPtr3{make_shared<int>(300)};
    return 0;
}

通過 .reset( ptr )

使用.reset( ptr ),使得釋放對shared_ptr原管理對象的所有權,轉為對新對象管理的所有權。

void reset() noexcept;

template< class Y >
void reset( Y* ptr );

template< class Y, class Deleter >
void reset( Y* ptr, Deleter d );

template< class Y, class Deleter, class Alloc >
void reset( Y* ptr, Deleter d, Alloc alloc );

註意.reset( ptr )若傳入的所指向的對象已被占有,程式會異常。

// ...
int main()
{
    shared_ptr<int> sPtr1(new int(100));
    shared_ptr<int> sPtr2 = sPtr1;
    shared_ptr<int> sPtr3 = sPtr2;
    shared_ptr<int> sPtr4 = sPtr1;
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count() << endl << endl;
    
    sPtr4.reset();
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count() << endl << endl;
    
    sPtr3.reset(new int(100));
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count();
    
    std::shared_ptr<int> sPtr5;
    sPtr5.reset(sPtr1.get()); // 異常
    return 0;
}
/** 輸出:
sPtr1 use count = 4
sPtr2 use count = 4
sPtr3 use count = 4
sPtr4 use count = 4

sPtr1 use count = 3
sPtr2 use count = 3
sPtr3 use count = 3
sPtr4 use count = 0

sPtr1 use count = 2
sPtr2 use count = 2
sPtr3 use count = 1
sPtr4 use count = 0
**/

解釋:

  1. 通過複製構造函數後,sPtr1-4的引用計數均為 4。
  2. sPtr4使用了reset()後,釋放被管理對象的所有權,被管理對象的shared_ptr個數減 1,sPtr4被設置為nullptr,同時,sPtr1-3的引用計數均變為 3。
  3. sPtr3使用了reset(new int(100))後,釋放原被管理對象的所有權,被管理對象的shared_ptr個數減 1,sPtr4被設置為新的管理對象int(100),同時,sPtr1-3的引用計數均變為 2。
  4. sPtr5reset傳入的所指向的對象已被占有,程式異常,沒有正常結束。

.reset() 釋放被管理對象的所有權

用於取消shared_ptr對管理對象的所有權;
當這個對象被shared_ptr管理的數量為 0,會執行刪除器。

使用.reset(),會使得shared_ptr設置為nullptr

獲取原始儲存的指針

T* get() const noexcept;

解引用存儲的指針 operator* and operator->

可以像普通指針一樣,使用shared_ptr對所管理的對象進行訪問。

// ...
int main()
{
    shared_ptr<int> sPtr1 {new int(100)};
    cout << *sPtr1 << endl << endl;
    
    int *i = sPtr1.get();
    cout << *i << endl;
    cout << *sPtr1 << endl << endl;
    
    *i = 200;
    cout << *i << endl;
    cout << *sPtr1 << endl << endl;
    
    *sPtr1 = 300;
    cout << *i << endl;
    cout << *sPtr1;
    return 0;
}
/** 輸出:
100

100
100

200
200

300
300
**/

直觀展示自動管理記憶體

// ...
int main()
{
    shared_ptr<Person> sPtr1 {make_shared<Person>()};
    shared_ptr<Person> sPtr2 {make_shared<Person>(18)};
    shared_ptr<Person> sPtr3 {make_shared<Person>(22)};
    
    shared_ptr<Person> sPtr4 = sPtr1;
    sPtr1.reset();
    sPtr2.reset();
    
    sPtr3.reset(new Person(19));
    
    cout << endl << "Before return" << endl;
    return 0;
}
/** 輸出:
Constructor: person's age = 0
Constructor: person's age = 18
Constructor: person's age = 22
Destructor: person's age = 18
Constructor: person's age = 19
Destructor: person's age = 22

Before return
Destructor: person's age = 0
Destructor: person's age = 19

**/

解釋:

  1. 定義了三個shared_ptrsPtr1sPtr2sPtr3,它們管理的對象分別為age = 0age = 18age = 22,列印了三行Person對象的構造函數中的輸出
  2. 通過賦值運算符,使得sPtr4同時管理sPtr1管理的對象
  3. 釋放sPtr1被管理對象的所有權,此時因為sPtr4還在管理原sPtr1管理的對象age = 0,因此age = 0對象並沒有被析構
  4. 釋放sPtr2被管理對象的所有權,此時因為age = 18對象沒有任何shared_ptr進行管理,age = 18對象被析構,列印了age = 18對象的析構函數中的輸出
  5. 釋放sPtr3被管理對象的所有權,sPtr3轉為管理age = 19的對象;是先構造age = 19對象,後再釋放sPtr3被管理對象的所有權,列印了age = 19對象的構造函數中的輸出;此時因為sPtr3原管理的age = 22對象沒有任何shared_ptr進行管理,age = 22對象被析構,列印了age = 22對象的析構函數中的輸出
  6. 在程式返回前Before return,管理age = 0age = 19對象的智能指針sPtr4sPtr3被析構,age = 0age = 19對象沒有智能指針管理,age = 0age = 19對象被析構,列印了它們對象的析構函數中的輸出。

別名

template< class Y >
shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;

別名用於訪問類的成員變數。

我們需要訪問的是某個實例的成員,因此並不希望在使用這個成員的時候,對應的實例被銷毀了。

使用別名的shared_ptr,增加了對這個實例的控制權,但仍然訪問的是成員。

// ...
struct Person{ int age = 18; };
struct Student{ Person person; };

int main(){
    shared_ptr<Student> studentPtr{make_shared<Student>()};
    cout << "studentPtr use count = " << studentPtr.use_count() << endl;
    shared_ptr<Person> personPtr{studentPtr, &(studentPtr->person)};
    cout << "studentPtr use count = " << studentPtr.use_count() << endl;
    cout << personPtr->age << endl;
    return 0;
}
/** 輸出:
studentPtr use count = 1
studentPtr use count = 2
18

**/

shared_ptr 與函數

按值傳遞

int main(){
	auto func = [](shared_ptr<int> sPtr){
        cout << "value = " << *sPtr << endl;
        cout << "enter func: use count = " << sPtr.use_count() << endl;
    };
    
    auto sPtr = make_shared<int>(100);
    cout << "init: use count = " << sPtr.use_count() << endl;
    func(sPtr);
    cout << "exit func: use count = " << sPtr.use_count() << endl;
    return 0;
}
/** 輸出:
init: use count = 1
value = 100
enter func: use count = 2
exit func: use count = 1

**/

按值傳遞,函數會複製一份參數,因此傳入的sPtr會被覆制一份,造成其對象的引用計數增加 1;
執行完這個函數,複製的那一份sPtr被銷毀,使得其對象的引用計數減少 1;

按引用傳遞

int main(){
	auto func = [](shared_ptr<int> &sPtr){
        cout << "value = " << *sPtr << endl;
        cout << "enter func: use count = " << sPtr.use_count() << endl;
    };
    
    auto sPtr = make_shared<int>(100);
    cout << "init: use count = " << sPtr.use_count() << endl;
    func(sPtr);
    cout << "exit func: use count = " << sPtr.use_count() << endl;
    return 0;
}
/** 輸出:
init: use count = 1
value = 100
enter func: use count = 1
exit func: use count = 1

**/

按引用傳遞,函數不會複製一份參數;
因此若函數內部無其它導致增加引用計數的操作,函數執行過程中引用計數都不會改變。

按引用傳遞,但為const

int main(){
    auto func = [](const shared_ptr<int> &sPtr){
        cout << "value = " << *sPtr << endl;
        cout << "enter func: use count = " << sPtr.use_count() << endl;
        sPtr.reset(); // error
        sPtr.reset(new Person()); // error
        sPtr.release(); // error
    };
    
    auto sPtr = make_shared<int>(100);
    cout << "init: use count = " << sPtr.use_count() << endl;
    func(sPtr);
    cout << "exit func: use count = " << sPtr.use_count() << endl;
    return 0;
}

使用const的引用傳遞,不能改變shared_ptr所管理的對象是哪一個,使用.reset()等都會造成編譯錯誤。

返回值為shared_ptr

int main(){
	auto createSPtr = [](int i) -> shared_ptr<Person>{
        shared_ptr<Person> sPtr = make_shared<Person>(i);
        cout << "age = " << i << " use count = " << sPtr.use_count() << endl;
        return sPtr;
    };
    shared_ptr<Person> sPtr = createSPtr(100);
    sPtr->getAge(); cout << "use count = " << sPtr.use_count() << endl;

    // 用作鏈式函數
    createSPtr(200)->getAge();
    
    cout << endl << "Before main return" << endl;
    return 0;
}
/** 輸出:
Constructor: person's age = 100
age = 100 use count = 1
Person's age = 100
use count = 1
Constructor: person's age = 200
age = 200 use count = 1
Person's age = 200
Destructor: person's age = 200

Before main return
Destructor: person's age = 100

**/

可見當用作鏈式函數時,使用完畢後,unique_ptr會被銷毀,同時被管理的對象也被析構。

管理動態數組需要指定刪除器

shared_ptr 的預設刪除器不支持釋放數組對象,需要指定刪除器。

例,一維數組指定刪除器

    // ...
    shared_ptr<int> ptr(new int[10], [](int* p) {delete[] p; });

同時,也可以使用 std::default_delete<T>() 函數作為刪除器,這個函數內部的刪除功能是通過delete釋放,T 為釋放什麼類型的記憶體的類型。

例,一維數組指定刪除器

    // ...
    shared_ptr<int> ptr(new int[10], default_delete<int[]>());

可以自己封裝模板函數來使 shared_ptr 支持釋放數組對象。

// ...
template <typename T>
shared_ptr<T> arrayShared_ptr(size_t size)
{
    // 返回匿名對象
    return shared_ptr<T>(new T[size], default_delete<T[]>());
}

int main()
{
    shared_ptr<int> sPtr1 = arrayShared_ptr<int>(100);
    shared_ptr<int> sPtr2 = arrayShared_ptr<char>(200);
    return 0;
}

危險行為

仍然可使用delete釋放智能指針管理的對象的地址

可以使用delete釋放shared_ptr管理的對象的地址,但是其它共用指針仍然可能會訪問這塊地址,若訪問了則會出現程式異常,因此應避免使用手動的delete

    // ...
    shared_ptr<int> sPtr1 {new int(100)};
    shared_ptr<int> sPtr2 = sPtr1;
    delete sPtr1.get();
    cout << *sPtr2 << endl; // error,運行時異常

同時存在原始指針和智能指針

如果一個地址記憶體,同時有原始指針和shared_ptr指向它,即使當所有shared_ptr都被銷毀,原始指針還依然存在的情況下,這個地址記憶體仍然會被釋放,若再用原始指針去訪問這個記憶體地址就是訪問了一塊未知地址的內容。

    // ...
	int *i1 = new int{100};
    shared_ptr<int> sPtr1{i1};
    int *i2 = sPtr1.get();
    cout << *i1 << endl;
    cout << *i2 << endl << endl;
    sPtr1.reset();
    cout << *i1 << endl; // 危險行為 
    cout << *i2 << endl; // 危險行為 
	return 0;
/** 輸出:
100
100

1664688 // 這是隨機的,訪問了一塊未知地址的內容
1664688 // 這是隨機的,訪問了一塊未知地址的內容

**/

同時,無論是使用shared_ptr,還是其它的智能指針,都應該避免與原始指針混用。

避免同時存在原始指針和智能指針的解決方案:

    // ...
    int *i = new int{100};
    shared_ptr<int> sPtr1{i};
    i = nullptr;
    delete i;
    cout << *sPtr1 << endl; // ok

使用一個原始指針初始化多個shared_ptr

不能使用一個原始指針初始化多個shared_ptr

    // ...
    int *i = new int{100};
    shared_ptr<int> sPtr1{i};
    shared_ptr<int> sPtr2{i}; // error 編譯通過 運行錯誤

最後

shared_ptr 由於使用引用計數,因此會造成額外的記憶體和性能開銷,因此在性能要求極為苛刻的情況下不適用。

unique_ptr是 0 開銷的智能指針,也能夠自動管理記憶體,但不會造成性能損失。


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

-Advertisement-
Play Games
更多相關文章
  • 抽獎程式 ''' 抽獎程式 使用時可以修改嘉賓名單,然後單機‘開始’和‘停止’按鈕 來控制界面上名單的滾動實現抽獎功能,涉及的模塊主要 有多線程 ''' import itertools import random import threading import time import tkinte ...
  • 摘要:本文主要講解圖像局部直方圖均衡化和自動色彩均衡化處理。這些演算法可以廣泛應用於圖像增強、圖像去噪、圖像去霧等領域。 本文分享自華為雲社區《[Python從零到壹] 五十四.圖像增強及運算篇之局部直方圖均衡化和自動色彩均衡化處理》,作者: eastmount。 一.局部直方圖均衡化 前文通過調用O ...
  • 簡介 限流顧名思義是對流量大小進行限制,防止請求數量超過系統的負載能力,導致系統崩潰,起到保護作用。 現實生活中限流也隨處可見,節假日出門旅行的人數會劇增,對於旅游景點來說往往會不堪重負,如果不進行人數控制,對整個景點的壓力會非常大,游客的體驗也會非常差,還容易出現安全事故等危險。 同樣的在一線城市 ...
  • 轉載:https://blog.csdn.net/tslx1020/article/details/128250777 1、spawn - 冷啟動 frida-trace -U -f com.apple.ExampleCode -m “+[NSURL URLWithString:]" 2、attac ...
  • 伺服器信息 在阿裡雲買了個搶占式的伺服器,地區為華南廣州,系統為Ubuntu 20.04,8核16GB。 安裝Docker 命令如下: $ apt-get update -y $ apt-get upgrade -y $ apt-get install -y docker.io 安裝成功後,檢查一下 ...
  • 2023-01-14 一、Spring底層IOC實現 1、IOC:將對象的控制器反轉給Spring 2、BeanFactory與ApplicationContext (1)BeanFactory:IOC容器的基本實現,是Spring內部的使用介面,是面向Spring本身的,不是提供給開發人員使用的。 ...
  • 牛牛剛剛出生,嗷嗷待哺,一開始他只能學說簡單的數字,你跟他說一個整數,他立刻就能學會。輸入一個整數,輸出這個整數。 ...
  • 本篇文章,我們就一起聊一聊如何來更好的使用緩存,探尋下如何降低緩存交互過程的性能損耗、如何壓縮緩存的存儲空間占用、如何保證多個操作命令原子性等問題的解決策略,讓緩存在項目中可以發揮出更佳的效果。 ...
一周排行
    -Advertisement-
    Play Games
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...