主頁 > 後端開發 > C++ 從&到&&

C++ 從&到&&

2022-01-17 16:54:32 後端開發

人類發展史,就是不斷挖坑、填坑的程序,
語言發展史也是如此!
任何一門設計合理的語言,給你的限制或提供的什么特性,都不是沒有代價的,

C的指標

指標:pointer
指標的思想起源于匯編,指標思想是編程思想歷史上的重大飛躍,
每一個編程語言都使用指標,C語言將指標完全暴露給了用戶,潘多拉之盒,

使用指標的必要性:資源管理,即地址管理,

思想層:將地址包了一層,
語法層:T *p; *p;
編譯器:包含一個intptr_t型別成員的結構體,
匯編層:暫存器間接尋址MOV,

image

C語言中只有一種引數傳遞方式:值傳遞,
void f(int p)
void f(int *p)

利用指標交換兩個數字

#include <stdio.h>
void Swap(int *p1,int *p2){
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}
int main(){
    int a = 10;
    int b = 20;
    Swap(&a,&b);
    printf("%d %d\n",a,b);
    return 0;
}

指標的級數
Int *p; int **p; int ***p;
理論上無限級,無限套娃,實際上受編譯器限制,

指標是一扇門,推開門,后面是整個世界,

C++的參考

參考:reference
已存在變數的別名,

使用參考的必要性:資源使用

思想層:受限制的指標,
語法層:T a; T &p=a;
編譯器:給你做了保證,一定是經過初始化的指標
匯編層:和指標一樣,

在匯編層,指標和參考是完全一樣的,
參考是一個語法糖,T a; T &p=a; 等價于 T *const p = &a

    int x=0;
00676664  mov         dword ptr [x],0  
    int &a = x;
0067666B  lea         eax,[x]  
0067666E  mov         dword ptr [a],eax  
    a = 1;
00676671  mov         eax,dword ptr [a]  
00676674  mov         dword ptr [eax],1  
    int *p = &x;
0067667A  lea         eax,[x]  
0067667D  mov         dword ptr [p],eax  
    *p = 2;
00676680  mov         eax,dword ptr [p]  
00676683  mov         dword ptr [eax],2  

    int *const p2 = &x;
00676689  lea         eax,[x]  
0067668C  mov         dword ptr [p2],eax  
    *p2 = 3;
0067668F  mov         eax,dword ptr [p2]  
00676692  mov         dword ptr [eax],3 

參考的情況:

int a = 1;
const int b = 1;
int &ref1 = a;
int &ref2 = 1;//ERROR
const int &ref3 = b;
const int &ref4 = 1;

Q:唯獨int &ref2 = 1;//ERROR?
A:C++的早期這種語法是被允許的,但是在函式呼叫傳引數時,會給程式員帶來誤解,于是后面就禁止了這種語法,

參考規則的特例:const參考

void f(int &i){}
void f(const int &i){}

int main(){
    int i = 1;
    f(i);//call f(int &i)
    f(2);//call f(const int &i)
    return 0;
}
void f(int &i){}
//void f(const int &i){}

int main(){
    int i = 1;
    f(i);//call f(int &i)
    f(2);//ERROR
    return 0;
}
//void f(int &i){}
void f(const int &i){}

int main(){
    int i = 1;
    f(i);//call f(const int &i)
    f(2);//call f(const int &i)
    return 0;
}

C++語言中就有了新的引數傳遞方式:參考傳遞 void f(T &p) ,實質也是傳值,

自定義型別最好用參考傳遞,可以避免不必要的建構式和解構式的呼叫,
內置型別建議用值傳遞,自定義型別建議用參考傳遞,內置型別,值傳遞會比按參考傳遞更高效,

解釋見:這里

利用參考交換兩個數字

#include <iostream>
#include <stdlib.h>
using namespace std;
void swap(int &a, int &b){
    int tmp = a;
    a = b;
    b = tmp;
}
int main(){
    int a = 3;
    int b = 4;
    swap(a, b);
    cout << "a=" << a<<" " << "b=" << b << endl;
    return 0;
}

參考的級數
只能一級,參考的物件必須是一個已存在的地址,參考變數本身的地址,外界不能訪問,
References are not objects; they do not necessarily occupy storage,
Because references are not objects, there are no arrays of references, no pointers to references, and no references to references,
int& a[3]; // ERROR
int&* p; // ERROR
int& &r; // ERROR

參考和指標疊加
int a; int *p = &a; int *&r = p; //OK

使用參考的場景:

  • 給函式傳遞可變引數
  • 給函式傳遞大型物件
  • 參考函式回傳值;

Q:參考能實作的基本上指標都可以實作,那為什么C++還需要引入參考呢?
A:最初主要是為了支持運算子多載,
c = a + b是可以接受的寫法,而c = &a + &b 就不是很方便而且有歧義了,
寫法上的方便是要第一考慮的,

Q:C++引入了參考,那為什么C++不和Java一樣讓指標對使用者不可見呢?
A:歷史原因,為了兼容C語言,程式員是自由的,

Q:C++為什么選擇&作為參考的識別符號?
A:需要用一個符號告訴編譯器,傳的是參考,&在C語言中是取地址的作用,于是就選擇了它,

Q:this為什么是指標型別,而不是參考型別?
A:歷史原因,this誕生早于參考,某種意義上來講,this應該被設計為參考型別,

Q:Why is "this" not a reference?
A:Because "this" was introduced into C++ (really into C with Classes) before references were added. Also, I chose "this" to follow Simula usage, rather than the (later) Smalltalk use of "self".

Q:拷貝建構式引數一定是參考,不然編譯通不過,為什么?
A:因為在入參的程序中,如果不是參考,會首先進行一次值拷貝;而要實作的就是拷貝構造,就會造成不斷的遞回最后爆炸,

Q:參考是受限制的指標,哪里受限制了?
A:參考變數本身的地址外界不可獲得,當然編譯器是可以的,

Q:參考變數是否占有記憶體空間?
A:參考可以不占用,也可以占有,語法規定對參考變數的操作都是對被參考物件的操作,

struct S {
    int a;
    int &b;
};
int x = 123;
S s(123,x);

sizeof(S)=?//32位環境等于8

non-const參考的匯編視角
image

const參考的匯編視角
image

說明:const參考變數系結沒有地址的物件時,會生成一個臨時變數/匿名物件來中轉,

全域定義
const int& a = 123; 123的匿名物件在堆上

區域定義
void f{
    const int& a = 456; 456的匿名物件在堆疊上
}

往下走用*,往上走用&,

C++的第一個坑:兼容了C語言的指標,

C++的構造

3種構造語意:

  1. 建構式constructor
  2. 拷貝構造copy constructor
  3. 拷貝賦值運算子copy assignment operator

建構式S()
出廠設定
拷貝構造S(const S &other)
把A的資料復制給B,B(A);
拷貝賦值運算子S& operator=(const S &other)
先把B的資源釋放,再把A的資料復制給B,B=A;

變數按記憶體分為兩種:

  1. 不包含指標,trivial type,籃球,
  2. 包含指標,handle type,風箏和風箏線,

拷貝的分類

  • 淺拷貝
    參考語意(reference semantics)
    缺陷:若寫法不對,可能會發生double free,
    Q:為什么編譯器所有的默認的行為都是淺拷貝?
    A:深拷貝不一定能實作,指向的物件可能是多型的,也可能是陣列,也可能有回圈參考,所以只能留待成員變數的類來決定怎樣實作復制,
    有時候為了防止默認拷貝發生,可以宣告一個私有的拷貝建構式,這種做法比較常見,但不可取,
  • 深拷貝
    值語意(value semantics)
    缺陷:出現了額外的構造和析構,性能損失,
    深拷貝和淺拷貝的本質區別就是兩個物件的行為屬性是否是獨立變化的,

C++的第二個坑:拷貝建構式

思考:
T為handle type,T A(...),T B(A),A賦值給B,如果A不再使用了,能不能讓B直接接管A的所有資源呢?(移動語意move semantics)
在不破壞現有語法規則情況下,你會如何設計?

  1. C++03現有語法不支持移動語意,需要新增移動語意,
  2. 如何標識物件的資源是可以被移動的呢?
  3. 這種機制必須以一種最低開銷的方式實作,并且對所有的類都有效,
  4. 設計在編譯層,與運行層面無關,

C++的設計者們注意到,大多數情況下,右值所包含的物件都是可以安全的被移動的,

左值與右值

左值和右值的概念
CPL語言引入了運算式值的型別value categories這種概念:左值和右值,left or right of assignment,
C語言沿用了類似的分類:左值和其他,locator value and other
C++98 沿用了C語言的分類,但是略微調整,引入了新定義:右值rvalue = https://www.cnblogs.com/txtp/archive/2022/01/17/non-lvalue,
C++11 新增了xvalue(an “eXpiring” value),并調整了之前左值和右值的定義,
image

(i)has identity: it's possible to determine whether the expression refers to the same entity as another expression, such as by comparing addresses of the objects or the functions they identify (obtained directly or indirectly);
(m)can be moved from: move constructor, move assignment operator, or another function overload that implements move semantics can bind to the expression.
準則 1:能不能分辨兩個運算式指的是同一個物體,比如我們可以通過比較地址,
準則 2:能不能使用移動語意,比如看看能不能用呼叫移動建構式,

  • i&~m:lvalue 左值
  • i&m:xvalue 將亡值
  • ~i&m:prvalue 純右值
  • i:glvalue泛左值
  • m:rvalue右值

C++17
分類和 C++11 是一樣的,但是語意上更加明確了,

  • glvalues:有自己地址的長壽物件
  • prvalues:為了初始化而用的短命物件
  • xvalue:資源已經不需要了,而且可以再利用的長壽物件

為了優化這樣一個情況:T(T(T(x)))==>T(x),將prvalues的定義略微調整了下,
具體可以參考Copy elision (復制消除)

左值參考和右值參考

T &Lref; // 左值參考,就是傳統的c++參考
T &&Rref; // 右值參考
Q:為什么使用&&做為右值參考的識別符號?
A:慣性設計,標準委員玩標點符號是真的可以,

規則:

  • non-const左值參考只能系結non-const左值
  • non-const右值參考只能系結non-const右值
  • const左值參考,可以系結任意,
  • const右值參考,可以系結non-const右值和const右值,注:這個使用的場景很少很少,

如何判定

namespace test {
    template <typename T> struct is_lvalue_reference {
        const static bool value = https://www.cnblogs.com/txtp/archive/2022/01/17/false;
    };
    template  struct is_lvalue_reference {
        const static bool value = true;
    };
    template  struct is_rvalue_reference {
        const static bool value = false;
    };
    template  struct is_rvalue_reference {
        const static bool value = true;
    };

    template  struct is_lvalue {
        const static bool value = is_lvalue_reference::value && (!is_rvalue_reference::value);
    };

    template  struct is_xvalue {
        const static bool value = (!is_lvalue_reference::value) && is_rvalue_reference::value;
    };

    template  struct is_prvalue {
        const static bool value = (!is_lvalue_reference::value && !is_rvalue_reference::value);
    };

    template  struct is_rvalue {
        const static bool value = (is_xvalue::value || is_prvalue::value);
    };

    template  struct is_glvalue {
        const static bool value = (is_xvalue::value || is_lvalue::value);
    };
}
struct Foo {};
Foo funRetFoo();
Foo &funRetFooLRef();
Foo &&funRetFooRRef();

TEST(TypeTraits, isRvalue) {
    //base type
    EXPECT_FALSE(::test::is_lvalue_reference<int>::value);
    EXPECT_FALSE(::test::is_rvalue_reference<int>::value);
    EXPECT_FALSE(::test::is_lvalue<int>::value);
    EXPECT_FALSE(::test::is_xvalue<int>::value);
    EXPECT_TRUE(::test::is_prvalue<int>::value);
    EXPECT_FALSE(::test::is_glvalue<int>::value);
    EXPECT_TRUE(::test::is_rvalue<int>::value);

    // return obj
    EXPECT_FALSE(::test::is_lvalue_reference<decltype(funRetFoo())>::value);
    EXPECT_FALSE(::test::is_rvalue_reference<decltype(funRetFoo())>::value);
    EXPECT_FALSE(::test::is_lvalue<decltype(funRetFoo())>::value);
    EXPECT_FALSE(::test::is_xvalue<decltype(funRetFoo())>::value);
    EXPECT_TRUE(::test::is_prvalue<decltype(funRetFoo())>::value);
    EXPECT_FALSE(::test::is_glvalue<decltype(funRetFoo())>::value);
    EXPECT_TRUE(::test::is_rvalue<decltype(funRetFoo())>::value);

    // return ref obj
    EXPECT_TRUE(::test::is_lvalue_reference<decltype(funRetFooLRef())>::value);
    EXPECT_FALSE(::test::is_rvalue_reference<decltype(funRetFooLRef())>::value);
    EXPECT_TRUE(::test::is_lvalue<decltype(funRetFooLRef())>::value);
    EXPECT_FALSE(::test::is_xvalue<decltype(funRetFooLRef())>::value);
    EXPECT_FALSE(::test::is_prvalue<decltype(funRetFooLRef())>::value);
    EXPECT_TRUE(::test::is_glvalue<decltype(funRetFooLRef())>::value);
    EXPECT_FALSE(::test::is_rvalue<decltype(funRetFooLRef())>::value);

    // return rref obj
    EXPECT_FALSE(::test::is_lvalue_reference<decltype(funRetFooRRef())>::value);
    EXPECT_TRUE(::test::is_rvalue_reference<decltype(funRetFooRRef())>::value);
    EXPECT_FALSE(::test::is_lvalue<decltype(funRetFooRRef())>::value);
    EXPECT_TRUE(::test::is_xvalue<decltype(funRetFooRRef())>::value);
    EXPECT_FALSE(::test::is_prvalue<decltype(funRetFooRRef())>::value);
    EXPECT_TRUE(::test::is_glvalue<decltype(funRetFooRRef())>::value);
    EXPECT_TRUE(::test::is_rvalue<decltype(funRetFooRRef())>::value);

    int lvalue;
    // 模擬=號左邊
    EXPECT_TRUE(::test::is_lvalue_reference<decltype(*&lvalue)>::value);
    EXPECT_FALSE(::test::is_rvalue_reference<decltype(*&lvalue)>::value);
    EXPECT_TRUE(::test::is_lvalue<decltype(*&lvalue)>::value);
    EXPECT_FALSE(::test::is_xvalue<decltype(*&lvalue)>::value);
    EXPECT_FALSE(::test::is_prvalue<decltype(*&lvalue)>::value);
    EXPECT_TRUE(::test::is_glvalue<decltype(*&lvalue)>::value);
    EXPECT_FALSE(::test::is_rvalue<decltype(*&lvalue)>::value);

    //operator++()
    EXPECT_FALSE(::test::is_lvalue_reference<decltype(lvalue++)>::value);
    EXPECT_FALSE(::test::is_rvalue_reference<decltype(lvalue++)>::value);
    EXPECT_FALSE(::test::is_lvalue<decltype(lvalue++)>::value);
    EXPECT_FALSE(::test::is_xvalue<decltype(lvalue++)>::value);
    EXPECT_TRUE(::test::is_prvalue<decltype(lvalue++)>::value);
    EXPECT_FALSE(::test::is_glvalue<decltype(lvalue++)>::value);
    EXPECT_TRUE(::test::is_rvalue<decltype(lvalue++)>::value);

    //operator++(int)
    EXPECT_TRUE(::test::is_lvalue_reference<decltype(++lvalue)>::value);
    EXPECT_FALSE(::test::is_rvalue_reference<decltype(++lvalue)>::value);
    EXPECT_TRUE(::test::is_lvalue<decltype(++lvalue)>::value);
    EXPECT_FALSE(::test::is_xvalue<decltype(++lvalue)>::value);
    EXPECT_FALSE(::test::is_prvalue<decltype(++lvalue)>::value);
    EXPECT_TRUE(::test::is_glvalue<decltype(++lvalue)>::value);
    EXPECT_FALSE(::test::is_rvalue<decltype(++lvalue)>::value);
}

記住一點:左值參考直接作用于lvalue,右值參考直接作用于xvalue,
Q:誰直接作用于prvalue呢?
A:只能間接作用:右值參考、const左值參考,生成一個指標型別的匿名變數做中轉,

移動語意

如何讓自定義物件支持移動語意?

  • 移動構造move constructorS(S &&other) noexcept
  • 移動賦值運算子move assignment operator S& operator=(S &&other) noexcept

note:需要加noexpect,
目的:告訴編譯器:這個移動函式不會拋出例外,可以放心地呼叫,否則,編譯器會呼叫拷貝函式,

C++11后,STL都支持了移動語意,移動語意對基本型別沒有性能提升的作用,

Q:如何將一個變數轉為右值參考?
A:static_cast<T &&>(t)
寫法一:

string s = "abcd";
string s1(static_cast<string &&>(s));
s1(s);
string s2 = static_cast<string &&>(s);

這種寫法麻煩,如何簡寫?模板,

寫法二:

//VS
template <class _Ty>
struct remove_reference<_Ty&&> {
    using type                 = _Ty;
    using _Const_thru_ref_type = const _Ty&&;
};

template <class _Ty>
using remove_reference_t = typename remove_reference<_Ty>::type;

// FUNCTION TEMPLATE move
template <class _Ty>
_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept { // forward _Arg as movable
    return static_cast<remove_reference_t<_Ty>&&>(_Arg);
}
string s = "abcd";
string s1(move(s));
string s2 = move(s);

由此誕生了std::move,在頭檔案中,

利用移動交換兩個數字

void swap(T &a1, T &a2){
    T tmp(std::move(a1)); // a1 轉為右值,移動建構式呼叫,低成本
    a1 = std::move(a2);   // a2 轉為右值,移動賦值函式呼叫,低成本
    a2 = std::move(tmp);  // tmp 轉為右值移動給a2
}

Q:move的引數是&&,為什么傳入左值也是可以的?
A:萬能參考

A=B B的資源給A了,A的資源自己處理掉的,如果B在外面繼續使用,則是未定義行為,

萬能參考

universal reference
概念:使用T&&型別的形參既能系結右值,又能系結左值,
T&&在代碼里并不總是右值參考,

萬能參考一定涉及到型別推導的,沒有型別推導就是右值參考,
具體規則:這篇文章
使用場景有2個:

template<typename T>
void f(T&& param); // param is a universal reference

auto&& var2 = var1; // var2 is a universal reference

參考折疊規則

reference-collapsing rules
引入該規則的原因:C++中禁止reference to reference,為了通過編譯器檢查,
當左值參考和右值參考同時出現在型別定義中時,需要如何處理?
約定:只有&& && = &&,沾上一個&就變左值參考了,

T && && ==>T &&
T && &  ==>T &
T &  && ==>T &
T &  &  ==>T &

Q:為什么要這么約定?
A:左值參考有副作用

函式內外引數型別不匹配

template<typename T>
void f(T&& a){
    g(a);  // 這里的 a 是什么型別?
}

// 版本 1
template<typename T>
void g(T &){ cout << "T&" << endl; }

// 版本 2
template<typename T>
void g(T &&){ cout << "T&&" << endl; }

int num;
f(0);
f(num);

輸出:
T&
T&

a是變數,是左值,所以輸出T&,
但是0是數字,是右值,為什么進去后就成了左值?能不能一致?
Q:一定要一致么?
A:在某些場景不一致會有問題,需要提供一種保證前后語意一致的機制,

Q:怎么才能實作一致呢?
A:還是static_cast,

template<typename T>
void f(T&& a){
    g(static_cast<T &&>(a));  
    //g(static_cast<T>(a));  這樣寫也可以
}

// 版本 1
template<typename T>
void g(T &){ cout << "T&" << endl; }

// 版本 2
template<typename T>
void g(T &&){ cout << "T&&" << endl; }

int a;
f(0);
f(a);

輸出:
T&&
T&

這種寫法麻煩,如何簡寫?模板,

Forward

轉發:某些函式需要將其中一個或多個實參連同型別不變地轉發給其他函式

//VS
// FUNCTION TEMPLATE forward
template <class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept { // forward an lvalue as either an lvalue or an rvalue
    return static_cast<_Ty&&>(_Arg);
}

template <class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept { // forward an rvalue as an rvalue
    static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
    return static_cast<_Ty&&>(_Arg);
}

就可以簡寫:

template<typename T>
void f(T&& a){
    g(forward<T>(a));  
}

好像也沒啥好處,就是static_cast替換為了forward,
區別:

  • static_cast 關鍵字
  • std::forward 模板函式,支持函式模板的變長引數,在頭檔案中,

例子:make_unique

// FUNCTION TEMPLATE make_unique
template <class _Ty, class... _Types, enable_if_t<!is_array_v<_Ty>, int> = 0>
_NODISCARD unique_ptr<_Ty> make_unique(_Types&&... _Args) { // make a unique_ptr
    return unique_ptr<_Ty>(new _Ty(_STD forward<_Types>(_Args)...));
}

template <class _Ty, enable_if_t<is_array_v<_Ty> && extent_v<_Ty> == 0, int> = 0>
_NODISCARD unique_ptr<_Ty> make_unique(size_t _Size) { // make a unique_ptr
    using _Elem = remove_extent_t<_Ty>;
    return unique_ptr<_Ty>(new _Elem[_Size]());
}

Perfect forward

轉發時,需要保持被轉發實參的所有性質不變:是否const、以及是左值還是右值,
完美轉發 = std::forward + 萬能參考 + 參考折疊

用途:一般作為多引數函式呼叫的中間層,

struct A{
    A(int &&n){ cout << "rvalue overload, n=" << n << endl; }
    A(int &n){ cout << "lvalue overload, n=" << n << endl; }
    A(const int &&n){ cout << "rvalue const overload, n=" << n << endl; }
    A(const int &n){ cout << "lvalue const overload, n=" << n << endl; }
};

class B{
public:
    template<typename T1, typename T2, typename T3, typename T4>
    B(T1 &&t1, T2 &&t2, T3 &&t3, T4 &&t4) :
        a1_(std::forward<T1>(t1)),
        a2_(std::forward<T2>(t2)),
        a3_(std::forward<T3>(t3)),
        a4_(std::forward<T4>(t4)) {}
private:
    A a1_, a2_, a3_, a4_;
};

int main(){
    int i = 1, j = 2, k = 3, m = 4;
    int &i_lref = i;
    const int &j_clref = j;
    int &&k_rref = std::move(k);
    const int &&m_crref = std::move(m);

    B b1(1, 2, 3, 4);
    B b2(i, j, k, m);
    B b3(i_lref, j_clref, k_rref, m_crref);
    B b4(i_lref, j_clref, std::move(k), static_cast<const int &&>(m));

    return 0;
}

直觀的感受:建構式只用寫一個,就可以滿足所有情形,代替了4^4=256種多載形式,

perfect的由來:引數傳遞的七種方案

不支持轉發的場景 EffectiveModernCppChinese/item30.md

匯編視角

第一個視角
image

右值參考就是一個指向一個匿名變數的指標,右值參考就是給了外界接觸這個匿名變數的機會,
可見初始化一個右值參考其實是開辟了兩塊空間,一塊是右值參考型別那么大的匿名變數,一塊是指向這個匿名變數的指標,

第二個視角
image

修改右值參考值的程序也分為兩步,取出指標的值,也就是匿名變數的地址,把右值賦值給地址所指的匿名變數,和修改指標的值一樣的,

Q:如何理解“右值參考延長了右值的生命周期”?
A:右值被放到了一個變數里,已經擁有了記憶體,當然就避免了被從暫存器里扔出去就消失的命運,這一塊記憶體的生命周期就和右值參考變數一致了,

最佳實踐

網上找的一個圖:
image

資源使用語意

老師給大寶一個球,大寶拿去玩,老師說:注意些別玩壞了,

球的狀態:可變:non-const,不可變:const,
意圖的體現,

大寶在玩球,小寶也想玩,怎么辦?
1. 小寶加入他,一起玩,
2. 大寶不給,小寶買了一個一模一樣的球,各玩各的,
3. 大寶說:我不玩了,給你玩吧,小寶接著玩,

需要三個語意來表達(實際上也足夠了):

1. 別名(二者唯一)link
2. 復制(二者各一)CTRL+C、CTRL+V,
3. 移動(一有一無)CTRL+X、CTRL+V,

別名對應參考,復制對應資源復制,移動對應資源轉移,
性能的體現,

C++實作
別名語意:reference
復制語意:copy constructor、copy assignment operator,
移動語意:move constructor、move assignment operator,

底層實作都是依賴指標,

設計原則

C++語言設計規則 摘自《C++語言的設計和演化》
"資源使用"上體現的原則:

  1. 全域平衡
  2. C++ 的發展必須由實際問題推動
  3. 對不用的東西不需要付出任何代價(零開銷規則)

簡單的東西依然保持簡單,

總結

C++的兩個坑:1.兼容了C語言的指標 2.拷貝建構式
參考解決了資源使用問題,但無法解決資源管理的問題,

指標是一個潘多拉盒子,要想解決根本問題,只能把盒子拋掉,這個對于C++來說不可能,
但是,C++在如何方便程式員做資源管理這個方向上也做了很多嘗試,

開放問題

Q:“指標思想”是所有編程語言的基石,為什么?
A:

參考資料

https://blog.csdn.net/l477918269/article/details/90233908
https://zhuanlan.zhihu.com/p/374392832
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm
https://en.cppreference.com/w/cpp/language/rule_of_three
https://stackoverflow.com/questions/3582001/what-are-the-main-purposes-of-using-stdforward-and-which-problems-it-solves
https://www.zhihu.com/question/363686723/answer/1910830503
https://blog.csdn.net/qq_33113661/article/details/89040579?
https://zhuanlan.zhihu.com/p/265778316
https://stackoverflow.com/questions/3582001/what-are-the-main-purposes-of-using-stdforward-and-which-problems-it-solves
https://www.cnblogs.com/xusd-null/p/3761637.html
https://zhuanlan.zhihu.com/p/265815272

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

標籤:其他

上一篇:2022年最新動力節點Java自學全套視瞥澩

下一篇:看到表弟為了看電影還在充銀子,我很心痛,于是用python給他寫了個免費看電影的軟體!

標籤雲
其他(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