1. 程式人生 > 實用技巧 >a review at smart pointer(前言)

a review at smart pointer(前言)

ref: MSDN

指標的定義:指標是儲存物件記憶體地址的變數。
C 和 C++ 指標的用法有:

  1. 分配堆記憶體的新物件
  2. 向一個函式傳遞另外一個函式
  3. 迭代陣列或其他資料結構中的元素

在 C 風格的程式設計中,裸指標適用於上述三種描述。但是,裸指標是導致很多問題的元凶,因此除非在有顯著效能優勢且清楚在刪除物件時哪個指標有最終的所有權時,不要用裸指標。

現代 C++ 提供智慧指標來負責分配物件、迭代器和遍歷資料結構們,以及傳遞函式時使用的 lambda 表示式

裸指標

指標是變數,它儲存物件在記憶體中的地址,它也用於訪問物件。裸指標的定義是:生命週期不受封裝物件控制的指標,這不同於智慧指標。裸指標可以被賦值為另一個非指標變數的地址,或者可以被賦值為 nullptr,未被賦值的裸指標隨機被賦值一個地址。

指標可以被取消引用(dereference),返回它指向的物件值。

int* p = nullptr; // declare pointer and initialize it
                      // so that it doesn't store a random address
int i = 5;
p = &i; // assign pointer to address of object
int j = *p; // dereference p to retrieve the value at its address

一個指標可以指向一個有型別的物件或者 void . 當一個程式在 heap 分配物件時,它接收這個物件的地址(以指標的形式),如此的指標叫做 owing pointer ,一個 owing pointer (or a copy of it),在不再需要那個 heap object 的時候,必須顯式的 free 掉這個指標。否則就會記憶體洩漏,即此記憶體的地址沒法給任何其他的程式去用了。用 new 分配出來的記憶體必須搭配 delete 或者 delete[]。

    MyClass* mc = new MyClass(); // allocate object on the heap
    mc->print(); // access class member
    delete mc; // delete object (please don't forget!)

一個指標,假設沒有被宣告為 const 指標,那麼就可以自增或者自減到記憶體中的其他位置,這種操作叫做指標的算術運算。在 C 語言中,陣列的迭代(或其他資料結構)會用到這種運算。const 指標不能變更到記憶體的其他位置。
在六十四位的作業系統中,指標就有六十四個 bit,一個指標的尺寸決定於它的可定址空間有多少。所有指標的拷貝都指向同一個記憶體地址,指標(同引用)在 C++ 程式中經常被用於傳遞物件的地址(而不是傳遞整個大物件),當定義一個函式的時候,規定指標引數為 const ,除非你想改這個物件,但是實際上,常引用比常量指標更常用於傳遞引數,除非此物件的值是 nullptr。
Pointers to functions允許函式被傳遞到其他函式中,在 C 語言中稱作 call backs,現代 C++ 轉而使用 lambda 表示式。

指標和陣列有很大的關聯,當一個數組在函式傳參的時候,它傳遞的是第一個元素的地址

#include <iostream>

void func(int arr[], int length)
{
    // returns pointer size. not useful here.
    size_t test = sizeof(arr);

    for(int i = 0; i < length; ++i)
    {
        std::cout << arr[i] << " ";
    }
}

int main()
{

    int i[5]{ 1,2,3,4,5 };
    // sizeof(i) = total bytes
    int j = sizeof(i) / sizeof(i[0]);
    func(i,j);
}

sizeof 操作符返回陣列有幾個 bytes,除以一個元素的 sizeof 就是陣列的長度,當一個數組被當成引數的時候,會變成一個指標,sizeof一個指標對於 x86 機器來說返回四個 byte,對於 x64 機器來說返回 8個 byte。
指標的算數運算可以用在非 const 指標讓它們指向其它記憶體地址,指標可以用++, +=, -=, --來自增或者自減,這種方法在沒型別的資料面前特別有用,比如 void*指標會自增一個 byte,一個有型別的指標按照它指向的值的 sizeof 自增。

void* 指標

(這段翻譯了也沒有原文效果好,不翻譯了)
A pointer to void simply points to a raw memory location. Sometimes it's necessary to use void* pointers, for example when passing between C++ code and C functions.

When a typed pointer is cast to a void pointer, the contents of the memory location are unchanged. However, the type information is lost, so that you can't do increment or decrement operations. A memory location can be cast, for example, from MyClass* to void* and back again to MyClass*. Such operations are inherently error-prone and require great care to avoid errors. Modern C++ discourages the use of void pointers in almost all circumstances.

Pointers to Functions

(這段無關緊要,不翻譯了)
In C-style programming, function pointers are used primarily to pass functions to other functions. This technique allows the caller to customize the behavior of a function without modifying it. In modern C++,lambda expressionsprovide the same capability with greater type safety and other advantages.

A function pointer declaration specifies the signature that the pointed-to function must have:

// Declare pointer to any function that...

// ...accepts a string and returns a string
string (*g)(string a);

// has no return value and no parameters
void (*x)();

// ...returns an int and takes three parameters
// of the specified types
int (*i)(int i, string s, double d);

const 和 volatile
const和volatile關鍵字改變了系統對待指標的方式,const指標規定在初始化之後其值不能被修改,也就是說指標被保護以至於不能被修改。volatile關鍵字規定了值可以被其他程式改變的變數(多執行緒?)因此,volatile關鍵字用於宣告shared memory中可以被多執行緒修改的或者全域性的資料(用於溝通的、用於interrupt service routines的)
當一個變數被宣告為volatile時,每一次程式要訪問這個變數時,編譯器都從記憶體重新讀取它的值,這樣顯著的減少了編譯器優化,however,當變數的狀態可以不尋常的改變時,這是唯一允許這個程式可以正常執行的方式。

這個blog寫的很好 https://www.cnblogs.com/chio/archive/2007/11/24/970632.html

To declare the object pointed to by the pointer asconstorvolatile, use a declaration of the form:
要將指標指向的物件宣告為const或volatile,請使用以下形式的宣告:

const char *cpch;
volatile char *vpch;

To declare the value of the pointer — that is, the actual address stored in the pointer — as const or volatile, use a declaration of the form:
宣告指標的值-也就是說,實際的地址存在了指標裡而不是指標指向的物件裡時,用這個宣告。

char * const pchc;
char * volatile pchv;

分割線,前方高能請撤離。

下一段我覺得誰也搞不懂,不翻譯了,感興趣自己去MSDN開發文件去

The C++ language prevents assignments that would allow modification of an object or pointer declared as const. Such assignments would remove the information that the object or pointer was declared with, thereby violating the intent of the original declaration. Consider the following declarations:

const char cch = 'A';
char ch = 'B';

Given the preceding declarations of two objects (cch, of type const char, and ch, of type char), the following declaration/initializations are valid:

const char *pch1 = &cch;
const char *const pch4 = &cch;
const char *pch5 = &ch;
char *pch6 = &ch;
char *const pch7 = &ch;
const char *const pch8 = &ch;
此處省略一萬字 參見https://docs.microsoft.com/en-us/cpp/cpp/const-and-volatile-pointers?view=msvc-160的後兩段。

new和delete

C++滋瓷動態物件的分配和銷燬,通過使用new和delete操作符,這些操作符在空閒區(free store)裡分配和析構物件。new操作符呼叫operator new,delete操作符呼叫operator delete。
C++標準庫裡的new函式支援記憶體分配失敗的時候丟擲一個std::bad_alloc,假設你要是不想要那個throw,你就要用黑魔法了。。。這輩子也用不到呵呵呵。。。

Thenewfunction in the C++ Standard Library supports the behavior specified in the C++ standard, which is to throw astd::bad_allocexception if the memory allocation fails. If you still want the non-throwing version ofnew, link your program withnothrownew.obj. However, when you link withnothrownew.obj, the defaultoperator newin the C++ Standard Library no longer functions.

For a list of the library files in the C Runtime Library and the C++ Standard Library, seeCRT Library Features.

new操作符

編譯器將呼叫operator new翻譯成這樣

char *pch = new char[BUFFER_SIZE];

重複呼叫operator new將返回不同的指標,假設記憶體不足,會丟擲一個std::bad_alloc,假設你用了黑魔法,那麼就返回一個nullptr
你也可以寫一個routine來釋放記憶體重新alloc,更多資訊參見Handling insufficient memory
You can write a routine that attempts to free memory and retry the allocation. For more information, see_set_new_handler. For details on the recovery scheme, see thesection.

operator new的第一個引數是size_t,在<stddef.h>中定義,返回型別是void* 。不想寫了,這段和smart pointer沒什麼關係,待更。。而且MSDN給的例子真是過於玄學。