中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

保定定興網(wǎng)站建設(shè)安卓手機優(yōu)化神器

保定定興網(wǎng)站建設(shè),安卓手機優(yōu)化神器,wordpress的按裝方法,東莞網(wǎng)站建設(shè)設(shè)計類和對象 1. 靜態(tài)對象的探討與全局對象的構(gòu)造順序 靜態(tài)對象的探討 類中的靜態(tài)成員變量(類類型靜態(tài)成員) 類中靜態(tài)變量的聲明與定義&#xff08;類中聲明類外定義&#xff09; #include<iostream> using namespace std;namespace _nmspl {class A{public:A():m_i(5){…

類和對象

1. 靜態(tài)對象的探討與全局對象的構(gòu)造順序

靜態(tài)對象的探討

類中的靜態(tài)成員變量(類類型靜態(tài)成員)
  • 類中靜態(tài)變量的聲明與定義(類中聲明類外定義
#include<iostream>
using namespace std;namespace _nmspl
{class A{public:A():m_i(5){cout << "A:A()缺省構(gòu)造函數(shù)執(zhí)行了" << endl;}~A(){cout << "A:~A()缺省析構(gòu)函數(shù)執(zhí)行了" << endl;}int m_i;};class B{public:static A m_sa; // 靜態(tài)成員變量的聲明};A B::m_sa; // 這是對類B靜態(tài)成員變量m_sa的定義
}int main()
{_nmspl::B bobj;cout << bobj.m_sa.m_i;return 0;
}
  • 沒有創(chuàng)建類B時
    • 類中靜態(tài)成員變量即使沒有被調(diào)用,也會被構(gòu)造和析構(gòu) 在這里插入圖片描述
  • inline
    • inline關(guān)鍵字最初用于建議編譯器嘗試將一個函數(shù)體直接插入到調(diào)用該函數(shù)的地方,以減少函數(shù)調(diào)用的開銷。這并不意味著編譯器一定會內(nèi)聯(lián)這個函數(shù),它只是對編譯器的一個提示。

    • 在C++17增加新用法,

      • 內(nèi)聯(lián)變量
      • 在這里插入圖片描述
      • 在這里插入圖片描述
      • 在這里插入圖片描述
    • 內(nèi)聯(lián)靜態(tài)成員變量

      • 在這里插入圖片描述
    • visual studio中改變C++標準

      • 在這里插入圖片描述
        #include<iostream>
        using namespace std;namespace _nmspl
        {class A{public:A():m_i(5){cout << "A:A()缺省構(gòu)造函數(shù)執(zhí)行了" << endl;}~A(){cout << "A:~A()缺省析構(gòu)函數(shù)執(zhí)行了" << endl;}int m_i;};//class B//{//public://	static A m_sa; // 靜態(tài)成員變量的聲明//};//A B::m_sa; // 這是對類B靜態(tài)成員變量m_sa的定義class B{public:inline static A m_sa; // 靜態(tài)成員即聲明又定義};
        }
        
函數(shù)中的靜態(tài)對象(類類型靜態(tài)對象)
  • 如果函數(shù)沒有被調(diào)用過,那么這個靜態(tài)對象不會被構(gòu)造,即使函數(shù)被調(diào)用多次,靜態(tài)對象也只會被創(chuàng)建依次

區(qū)別于:類中靜態(tài)成員變量即使沒有被調(diào)用,也會被構(gòu)造和析構(gòu)

全局對象的構(gòu)造順序問題

  • 全局對象的構(gòu)造順序不確定的
    • 在這里插入圖片描述
  • 注意不要出現(xiàn)構(gòu)造一個全局對象,需要另外一個全局對象,因為無法確定誰先被構(gòu)造
    • 出現(xiàn)錯誤在這里插入圖片描述
// class1.h
#ifndef __CLASS1_H__
#define __CLASS1_H__class Class1 {
public:Class1();~Class1();
};
#endif// class2.h
#ifndef __CLASS2_H__
#define __CLASS2_H__class Class2 {
public:Class2();~Class2();
public:int m_i;
};
#endif// class1.cpp
#include<iostream>
using namespace std;
#include"Class1.h"
#include"Class2.h"extern Class2 gclass2;
Class1::Class1() 
{cout << "調(diào)用Class2中的m_i=" << gclass2.m_i << endl;cout << "Class1:構(gòu)造函數(shù)()" << endl;
}
Class1::~Class1()
{cout << "Class1:析構(gòu)函數(shù)()" << endl;
}// class2.cpp
#include<iostream>
using namespace std;
#include"Class2.h"Class2::Class2():m_i(5)
{cout << "Class2:構(gòu)造函數(shù)()" << endl;
}
Class2::~Class2()
{cout << "Class2:析構(gòu)函數(shù)()" << endl;
}// main.cpp
#include<iostream>
using namespace std;
#include"Class1.h"
#include"Class2.h"
Class1 gclass1;
Class2 gclass2;
int main()
{return 0;
}
  • 如果需要可以使用函數(shù)進行構(gòu)造返回
    • 在這里插入圖片描述
// class1.h
#ifndef __CLASS1_H__
#define __CLASS1_H__class Class1 {
public:Class1();~Class1();
};
#endif// class2.h
#ifndef __CLASS2_H__
#define __CLASS2_H__class Class2 {
public:Class2();~Class2();
public:int m_i;
};
#endif// class1.cpp
#include<iostream>
using namespace std;
#include"Class1.h"
#include"Class2.h"#include"func.h"extern Class2 gclass2;
Class1::Class1() 
{cout << getClass2().m_i << endl;cout << "Class1:構(gòu)造函數(shù)()" << endl;
}
Class1::~Class1()
{cout << "Class1:析構(gòu)函數(shù)()" << endl;
}// class2.cpp
#include<iostream>
using namespace std;
#include"Class2.h"Class2& getClass2()
{static Class2 gclass2;   // 不要在多線程中調(diào)用return gclass2;
}
Class2::Class2():m_i(5)
{cout << "Class2:構(gòu)造函數(shù)()" << endl;
}
Class2::~Class2()
{cout << "Class2:析構(gòu)函數(shù)()" << endl;
}
// func.h
#ifndef __FUNC_H__
#define __FUNC_H__class Class2; // 類的前置聲明
Class2& getClass2();
#endif
// main.cpp
#include<iostream>
using namespace std;
#include"Class1.h"
#include"Class2.h"
Class1 gclass1;
//Class2 gclass2;
int main()
{return 0;
}

2. 拷貝構(gòu)造函數(shù)和拷貝賦值運算符

拷貝構(gòu)造函數(shù)和拷貝賦值運算符的書寫

#include<iostream>using namespace std;namespace _nmspl 
{class A{public:A() :m_caa(0), m_cab(0) {}//拷貝構(gòu)造函數(shù)A(const A& tmpobj){m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;}//拷貝賦值運算符重載A& operator+(const A& tmpobj){m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;return *this;}public:int m_caa;int m_cab;};
}
int main()
{
}

對象自我賦值產(chǎn)生的問題

#include<iostream>
#include<cstring>
using namespace std;namespace _nmspl 
{class A{public:A() :m_caa(0), m_cab(0) {}//拷貝構(gòu)造函數(shù)A(const A& tmpobj){m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;}//拷貝賦值運算符重載A& operator+(const A& tmpobj){m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;return *this;}public:int m_caa;int m_cab;};
}namespace _nmsp2
{class A{public:A() :m_caa(0), m_cab(0),m_cap(new char[100]) {}//拷貝構(gòu)造函數(shù)A(const A& tmpobj){m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;// 注意這個是錯誤的,拷貝構(gòu)造函數(shù)時內(nèi)存還未分配,直接new即可/*if (m_cap != NULL)  {delete[] m_cap;m_cap = NULL;}*/m_cap = new char[100];memcpy(m_cap,tmpobj.m_cap,100);}//拷貝賦值運算符重載A& operator+(const A& tmpobj){if (&tmpobj == this)return *this;// 注意這個是需要進行內(nèi)存釋放的,因為已經(jīng)調(diào)用過構(gòu)造函數(shù)了if (m_cap != NULL){delete[] m_cap;m_cap = NULL;}m_cap = new char[100];strcap(m_cap, tmpobj.m_cap);m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;return *this;}~A(){delete[] m_cap;			}public:int m_caa;int m_cab;char* m_cap;};
}
int main()
{
}

繼承關(guān)系下的拷貝構(gòu)造函數(shù)和拷貝賦值運算符的書寫

  • 關(guān)鍵點
    • 當又子類時,一定要將父類的析構(gòu)函數(shù)設(shè)置為虛函數(shù)。不然在多態(tài)時,子類的析構(gòu)函數(shù)不會被調(diào)用。
    • 當父類和子類同時都有拷貝構(gòu)造函數(shù)和賦值運算符重載函數(shù)時,子類一定要主動去調(diào)用父類的這兩個函數(shù)。不然父類的這兩個函數(shù)不會被調(diào)用。
  • 在C++中,將基類(父類)的析構(gòu)函數(shù)聲明為虛函數(shù)是非常重要的,特別是在涉及到多態(tài)和繼承的情況下。這樣做的主要原因是確保當通過基類指針刪除派生類對象時,能夠正確地調(diào)用派生類的析構(gòu)函數(shù),從而釋放派生類中可能分配的所有資源。這有助于避免內(nèi)存泄漏或其他資源管理的問題。
#include <iostream>class Base {
public:~Base() { std::cout << "Base destructor\n"; }
};class Derived : public Base {int* data;
public:Derived() { data = new int[10]; }  // 分配一些資源~Derived() { delete[] data; std::cout << "Derived destructor\n"; }
};int main() {Base* basePtr = new Derived();delete basePtr;  // 這里只調(diào)用了Base的析構(gòu)函數(shù)
}

在這個例子中,Base 類的析構(gòu)函數(shù)不是虛函數(shù)。當我們通過 Base* 指針刪除 Derived 對象時,只有 Base 的析構(gòu)函數(shù)被調(diào)用。這意味著 Derived 類中的資源(即 data 數(shù)組)沒有被釋放,導致了內(nèi)存泄漏。

  • 當子類B為空時
    #define _CRT_SECURE_NO_WARNINGS
    #include<iostream>
    #include<cstring>
    using namespace std;namespace _nmspl 
    {class A{public:A() :m_caa(0), m_cab(0) {}//拷貝構(gòu)造函數(shù)A(const A& tmpobj){m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;}//拷貝賦值運算符重載A& operator+(const A& tmpobj){m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;return *this;}public:int m_caa;int m_cab;};
    }namespace _nmsp2
    {class A{public:A() :m_caa(0), m_cab(0),m_cap(new char[100]) {}//拷貝構(gòu)造函數(shù)A(const A& tmpobj){m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;// 注意這個是錯誤的,拷貝構(gòu)造函數(shù)時內(nèi)存還未分配,直接new即可/*if (m_cap != NULL)  {delete[] m_cap;m_cap = NULL;}*/m_cap = new char[100];memcpy(m_cap,tmpobj.m_cap,100);}//拷貝賦值運算符重載A& operator+(const A& tmpobj){if (&tmpobj == this)return *this;// 注意這個是需要進行內(nèi)存釋放的,因為已經(jīng)調(diào)用過構(gòu)造函數(shù)了if (m_cap != NULL){delete[] m_cap;m_cap = NULL;}m_cap = new char[100];memcpy(m_cap, tmpobj.m_cap,100);m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;return *this;}virtual ~A(){delete[] m_cap;			}public:int m_caa;int m_cab;char* m_cap;};class B:public A{};
    }
    int main()
    {_nmsp2::B bobj1;bobj1.m_caa = 100;bobj1.m_cab = 200;strcpy(bobj1.m_cap,"new class");_nmsp2::B bobj2 = bobj1;  // 執(zhí)行類A的拷貝構(gòu)造函數(shù)bobj2 = bobj1; // 執(zhí)行類A的拷貝賦值運算符
    }
    
  • 當子類B有自己的拷貝和賦值;
    #define _CRT_SECURE_NO_WARNINGS
    #include<iostream>
    #include<cstring>
    using namespace std;namespace _nmspl 
    {class A{public:A() :m_caa(0), m_cab(0) {}//拷貝構(gòu)造函數(shù)A(const A& tmpobj){m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;}//拷貝賦值運算符重載A& operator+(const A& tmpobj){m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;return *this;}public:int m_caa;int m_cab;};
    }namespace _nmsp2
    {class A{public:A() :m_caa(0), m_cab(0),m_cap(new char[100]) {}//拷貝構(gòu)造函數(shù)A(const A& tmpobj){m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;// 注意這個是錯誤的,拷貝構(gòu)造函數(shù)時內(nèi)存還未分配,直接new即可/*if (m_cap != NULL)  {delete[] m_cap;m_cap = NULL;}*/m_cap = new char[100];memcpy(m_cap,tmpobj.m_cap,100);cout << "父類的拷貝構(gòu)造函數(shù)" << endl;}//拷貝賦值運算符重載A& operator+(const A& tmpobj){if (&tmpobj == this)return *this;// 注意這個是需要進行內(nèi)存釋放的,因為已經(jīng)調(diào)用過構(gòu)造函數(shù)了if (m_cap != NULL){delete[] m_cap;m_cap = NULL;}m_cap = new char[100];memcpy(m_cap, tmpobj.m_cap,100);m_caa = tmpobj.m_caa;m_cab = tmpobj.m_cab;cout << "父類的拷貝賦值運算符" << endl;return *this;}virtual ~A(){delete[] m_cap;			}public:int m_caa;int m_cab;char* m_cap;};class B:public A{public:B() = default;B(const B& b){cout << "子類的拷貝構(gòu)造函數(shù)" << endl;}void operator=(const B& b){cout << "子類的拷貝賦值運算符" << endl;//return B();}};
    }
    int main()
    {_nmsp2::B bobj1;bobj1.m_caa = 100;bobj1.m_cab = 200;strcpy(bobj1.m_cap,"new class");_nmsp2::B bobj2 = bobj1;  // 只調(diào)用子類的拷貝構(gòu)造函數(shù)bobj2 = bobj1; // 只調(diào)用子類的拷貝賦值運算符
    }
    

只調(diào)用子類的函數(shù)

  • 需要程序自己主動去調(diào)用父類的拷貝構(gòu)造函數(shù)與拷貝賦值運算符函數(shù)
    class B : public A{public:B() = default;B(const B& b) : A(b){cout << "子類的拷貝構(gòu)造函數(shù)" << endl;}B& operator=(const B& b){A::operator=(b);cout << "子類的拷貝賦值運算符" << endl;return *this;}};

注意:調(diào)用父類的構(gòu)造函數(shù)的錯誤寫法
B(const B& b)
{
A(b);//存在二義性,創(chuàng)建對象或者調(diào)用函數(shù)
cout << “子類的拷貝構(gòu)造函數(shù)” << endl;
}

  • 檢查內(nèi)存是否釋放(只有在F5才起作用)
    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
    int* p = new int[10];
    

在這里插入圖片描述

3. 類的public繼承(is-a關(guān)系)及代碼編寫規(guī)則

  • 子類繼承父類得方式-有三種:公有;受保護:私有繼承
    • public代表得是一種is-a(是一種)的關(guān)系。通過這個子類產(chǎn)生的對象也一定是一個父類對象。
    • 人類(人類),人類(男人):父類表現(xiàn)的是一種更泛化的概念,而子類表現(xiàn)得是一種更特化的概念.
    • public繼承關(guān)系的檢驗規(guī)則:能夠在父類對象上做的行為也必然能在子類對象上做,每個子類對象同時也都是一個父類對象。
    • 里氏替換(利斯科夫替換)原則:任何基類出現(xiàn)的地方都應該可以無差別的使用子類替換.

子類遮蔽父類的普通成員函數(shù)

  • 對于public繼承,不建議也不應該使用子類的普通成員函數(shù)遮蔽同名的父類的普通成員函數(shù)
  • 既然在父類中是普通成員函數(shù),那么就代表在子類中不會有不同的行為,代表的是一種不變性。
    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    #include <cstring>
    using namespace std;
    class Humain
    {
    public:void eat(){cout << "人類吃食物" << endl;}virtual ~Humain(){}
    };class Man :public Humain
    {
    public:void eat(){cout << "男人吃面試" << endl;}
    };
    int main()
    {Man man;man.eat(); // 調(diào)用子類的函數(shù)man.Humain::eat(); // 調(diào)用父類的成員函數(shù)
    }
    

父類的純虛函數(shù)接口

  • 純虛函數(shù),讓子類繼承父類的純虛函數(shù)接口。
  • 純虛函數(shù)
    • 擁有此函數(shù)的類變成了抽象類,抽象類不能生成該類對象
    • 任何繼承該類的類,必須實現(xiàn)這個純虛函數(shù)。

父類的虛函數(shù)接口

  • 虛函數(shù)讓子類繼承父類的虛函數(shù)接口和實現(xiàn),子類也可以提供實現(xiàn)

為純虛函數(shù)指定實現(xiàn)體

  • 為純虛函數(shù)指定實現(xiàn)體
    • 強制子類必須去實現(xiàn)該函數(shù)
    • 讓一些確實不需要單獨實現(xiàn)該接口的子類有機會直接調(diào)用父類的該實現(xiàn)體

類的public繼承(is-a關(guān)系)綜合范例

public繼承關(guān)系下的代碼編寫規(guī)則

4. 類與類之間的組合關(guān)系和委托關(guān)系

組合關(guān)系(復合關(guān)系-Compositon)

  • 一個類中的定義中含有其他類類型變量
has-a關(guān)系(is-part-os)
is-implemented-in-terms-of關(guān)系
  • 根據(jù)…實現(xiàn)…

// multimap:鍵可以重復
// 我們現(xiàn)在先去實現(xiàn)一個鍵不可以重復的map
// 繼承關(guān)系
//class MyMap :public multimap<T, U> {…};

template<typename T,typename U>
class MyMap
{
public:void insert(const T& key, const U & value){if (container.find(key) != container)return;container.insert(make_pair<T, U>(key, value));}size_t size(){return container.size();}
private:multimap<T, U> container;
};
組合關(guān)系的UML圖

在這里插入圖片描述
在這里插入圖片描述

實心菱形框 - 組合關(guān)系中的Human與Info對象具有相同的生命周期

委托關(guān)系(聚合關(guān)系:Deletation)

  • 一個類中具有指向?qū)幫庖粋€類的指針
    • 在這里插入圖片描述

空菱形框 - 生命周期不一樣

5. 類的private繼承探討

  • public繼承
    class Humain
    {
    public:
    };class Man :public Humain
    {
    public:
    };
    int main()
    {Man man;Humain & human = man;   // 父類引用綁定子類對象Humain* pHUman = &man;  //父類指針指向子類對象return 0;
    }
    
  • private繼承:就不屬于is-a關(guān)系了
    在這里插入圖片描述
  • private繼承是一種組合關(guān)系,是組合關(guān)系中的is-implemented-in-terms-of根據(jù)…實現(xiàn)…
  • 一般優(yōu)先考慮使用組合關(guān)系,只有在一些比較特殊的情況和必要的情況下,比如牽扯一些保護的成員、私有成員、虛函數(shù)等 案例如下
  • 在這里插入圖片描述

6. 不能被拷貝構(gòu)造和拷貝賦值的類對象

  • 給構(gòu)造函數(shù)寫delete,編譯器也不會自動生成默認的構(gòu)造函數(shù),需要程序員自己去寫
    class A
    {
    public:A(const& A a) = delete;
    };
    int main()
    {A a;  //報錯return 0;
    }
    

實現(xiàn)方案一:delete

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;class A
{
public:A(const& A a) = delete;A& operator=(const& A a)= delete;
};
int main()
{A a;return 0;
}

實現(xiàn)方案二:private

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;class A
{
private:A(const& A a) = delete;A& operator=(const& A a)= delete;
};
int main()
{A a;return 0;
}

但是類內(nèi)還是可以訪問這兩個函數(shù)

實現(xiàn)方案三:不提供實現(xiàn)

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;class A
{
public:A() = default;A(const A& a) ;A& operator=(const A& a);
};
int main()
{A a;A a1(a);a1 = a;return 0;
}

調(diào)用會出現(xiàn)鏈接錯誤

實現(xiàn)法案四:繼承Noncopyable成員

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;class Noncopyable
{
protected:Noncopyable() {};~Noncopyable() {};
private:Noncopyable(const Noncopyable& a) ;Noncopyable& operator=(const Noncopyable& a);
};
class A :private Noncopyable
{};
int main()
{A a;A a1(a);a1 = a;return 0;
}

7. 虛析構(gòu)函數(shù)的內(nèi)存泄露問題深談

  • 一個類如果不是父類,建議此類的析構(gòu)函數(shù)不要定義為虛析構(gòu)函數(shù)。因為這樣會因為虛函數(shù)表增一個虛函數(shù)表指針
  • 為什么會出現(xiàn)內(nèi)存泄露問題
    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    #include <cstring>
    using namespace std;class ThirdPart
    {
    public:ThirdPart() = default;~ThirdPart() {cout << "~ThirdPart()被調(diào)用" << endl;};
    };
    class A :public ThirdPart
    {
    public:int* m_p;A() {m_p = new int[100];}~A(){cout << "~A()被調(diào)用" << endl;delete m_p;}
    };
    int main()
    {ThirdPart * ths = new A();delete ths;return 0;
    }
    

不要隨便public繼承一個類

  • 一個類不可以被繼承:final
     class ThirdPart final
    {
    public:ThirdPart() = default;~ThirdPart() {cout << "~ThirdPart()被調(diào)用" << endl;};
    };
    

只有父類指針指向子類對象或者父類引用綁定到子類對象時,父類才需要虛析構(gòu)函數(shù)
如果子類private或protected繼承父類,那么父類指針不能指向子類對象,只能時public繼承,需要父類提供虛析構(gòu)函數(shù)

  • 一個函數(shù)的成員函數(shù)被聲明為非public中,在main函數(shù)不能被調(diào)用

    class A 
    {	~A(){cout << "~A" << endl;}
    };
    int main()
    {A* p = new A();   delete p;  // erro:注意這里是去調(diào)用A的析構(gòu)函數(shù),而A的析構(gòu)函數(shù)不能被調(diào)用return 0;
    }
    ``
  • 下面也就好理解了

    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    #include <cstring>
    using namespace std;class Noncopyable
    {
    protected:Noncopyable() {};~Noncopyable(){cout << "~Noncopyable" << endl;};
    private:Noncopyable(const Noncopyable& a);Noncopyable& operator=(const Noncopyable& a);
    };
    class A :public Noncopyable
    {	~A(){cout << "~Noncopyable" << endl;}
    };
    int main()
    {Noncopyable* p = new A();delete p;return 0;
    }
    

8. 類設(shè)計中的有些技巧

8.1 優(yōu)先考慮為成員變量提供訪問接口

class A
{
publicint m_a;
}class A
{
public :int getA(){return m_a;}
privateint m_a;
}

8.2 如何避免將父類的虛函數(shù)暴露給子類

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;class A
{
public:void fun(){func();}virtual ~A() {}
private:virtual void func(){cout << "A::func()" << endl;}
};class B:public A
{
public:B() = default;
private:virtual void func(){cout << "B::func()" << endl;}
};
int main()
{A* p = new B();p->fun(); //B::func()return 0;
}

fun函數(shù)是func虛函數(shù)的一行通道性質(zhì)的代碼。非虛擬接口(Nonvirtual Interface NVI)
如果能將虛函數(shù)設(shè)置為私有,則優(yōu)先考慮將其設(shè)置為私有

8.3 不要在類的構(gòu)造函數(shù)和析構(gòu)函數(shù)中調(diào)用虛函數(shù)

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;class A
{
public:void fu(){func1_vir();}A(){func1_vir();}virtual ~A() {func2_vir();}virtual void func1_vir(){cout << "A::func1_vir()" << endl;}virtual void func2_vir(){cout << "A::func2_vir()" << endl;}
};class B:public A
{
public:B(){func1_vir();}virtual ~B(){func2_vir();}virtual void func1_vir(){cout << "B::func1_vir()" << endl;}virtual void func2_vir(){cout << "B::func2_vir()" << endl;}
};
int main()
{A* p = new B();cout << "begin_____" << endl;p->func1_vir();p->func2_vir();p->fu();cout << "end_____" << endl;delete p;return 0;
/* 輸出結(jié)果
A::func1_vir()
B::func1_vir()
begin_____
B::func1_vir()
B::func2_vir()
B::func1_vir()
end_____
B::func2_vir()
A::func2_vir()*/
}

A::func1_vir() 父類中構(gòu)造函數(shù)調(diào)用的虛函數(shù)是父類的虛函數(shù)
B::func1_vir() 子類中構(gòu)造函數(shù)調(diào)用的虛函數(shù)是子類的虛函數(shù)
begin_____
B::func1_vir()
B::func2_vir()
B::func1_vir() 定義在父類中的非虛函數(shù)fu()中的虛函數(shù)調(diào)用的是子類的虛函數(shù)
end_____
B::func2_vir() 子類中析構(gòu)函數(shù)調(diào)用的虛函數(shù)是子類的虛函數(shù)
A::func2_vir() 父類中析構(gòu)函數(shù)調(diào)用的虛函數(shù)是父類的虛函數(shù)

如果在父類的構(gòu)造函數(shù)中調(diào)用了一個子類的虛函數(shù)是無法做到的,因為執(zhí)行到父類的構(gòu)造函數(shù)時對象的子類部分還沒有被構(gòu)造出來
如果在父類的析構(gòu)函數(shù)中調(diào)用一個子類的虛函數(shù)也是無法做到的,因為執(zhí)行到父類的析構(gòu)函數(shù)時對象的子類部分其實已經(jīng)被銷毀了
在構(gòu)造函數(shù)或析構(gòu)函數(shù)中,虛函數(shù)可能會失去虛函數(shù)的作用而被當作一個普通函數(shù)

8.4 析構(gòu)函數(shù)的虛與非虛談

  • 父類的析構(gòu)函數(shù)不一定必須是虛函數(shù),當父類指針指向子類或父類引用綁定子類時,父類需要寫一個public修飾的析構(gòu)函數(shù),這樣就可以通過父類的接口銷毀子類對象,否則會導致內(nèi)存泄漏
  • 用protect修飾析構(gòu)函數(shù)
    • 無法創(chuàng)建子類對象
    • 無法讓父類指針指向父類或者子類對象
  • 如果一個父類的析構(gòu)函數(shù)不是虛函數(shù),并且也不利用這個父類創(chuàng)建對象,也不會用到這個父類類型的指針,則應該考慮將父類的的析構(gòu)函數(shù)使用protected修飾 ,增加代碼安全性
  • 父類的析構(gòu)函數(shù)不是虛函數(shù),本身就暗示了不會通過父類的接口有銷毀子類的對象

8.5 抽象類的模擬

  • 抽象類要求至少有一個純虛函數(shù)
  • 抽象類:不能用來生成對象
  • 將模擬的抽象類的構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)都使用protected修飾
    class PVC
    {
    protected:PVC() {cout << "PVC()" << endl;}PVC(const PVC& pvc) {}
    };
    class SubPVC :public PVC
    {
    public:SubPVC(){cout << "SubPVC()" << endl;}
    };
    int main()
    {PVC* p = new SubPVC();  //  YesPVC* p = new PVC();  //  errorreturn 0;
    }
    
  • 將模擬的抽象類的析構(gòu)函數(shù)設(shè)置為純虛函數(shù),并在類外提供實現(xiàn)體(大多數(shù)純虛函數(shù)沒有實現(xiàn)體,但是純虛函數(shù)是個例外,為了釋放資源,所以一般要有一個實現(xiàn)體)
    class PVC
    {
    protected:PVC() {}virtual ~PVC() = 0;
    };
    PVC::~PVC()
    {}
    class SubPVC :public PVC
    {
    public:~SubPVC() {}
    };
    
  • 將模擬的抽象類的析構(gòu)函數(shù)使用protected修飾

8.6 盡量避免隱式類型轉(zhuǎn)換

  • 類型轉(zhuǎn)換構(gòu)造函數(shù)
    class A
    {
    public:A(int i){cout << "A()" << endl;}
    };int main()
    {A a = 5.2; // 將5構(gòu)造成一個臨時對象Areturn 0;
    }
    
  • explicit
    class A
    {
    public:explicit A(int i){cout << "A()" << endl;}
    };int main()
    {//A a = 5;  // errorA a = A(5); return 0;
    }
    

8.7 強制類對象不可以或只可以在堆上分配內(nèi)存

8.7.1 強制類對象不可以在堆上分配內(nèi)存
  • 重載類中的operator newoperator delete,并使用private修飾
    
    class A
    {
    public:
    private:static void* operator new(size_t size);static void operator delete(void *p);};int main()
    {A* a = new A();  // errorA* a = new A[3];  // 但是卻可以new數(shù)組return 0;
    }
    
  • 再次修改
    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    #include <cstring>
    using namespace std;class A
    {
    public:
    private:static void* operator new(size_t size);static void operator delete(void *p);static void* operator new[](size_t size);static void operator delete[](void* p);};int main()
    {A* a = new A[3];return 0;
    }
    
8.7.2 強制類對象只可以在堆上分配內(nèi)存
  • 使用private修飾析構(gòu)函數(shù)
class A
{
public:void destiry(){delete this;}
private:~A() {};  // 這樣寫也會導致創(chuàng)建在堆中的對象,不能delete。// 所以需要一個函數(shù)進行顯示的調(diào)用};int main()
{A a;  // errorA* p = new A();p->destiry();return 0;
}

9. 命名空間使用的一些注意事項

  • 使用using聲明命名空間的代碼不要放在.h文件中 ,否則會造成命名空間污染
  • .cpp文件中,using聲明語句放在include語句之后

10. 類定義的相互依賴與類的前向聲明

  • 前向聲明

    // a1.h
    #ifndef __A1_H__
    #define __A1_H__
    //#include"a2.h"
    class A2;
    class A1
    {
    public:A2* pm;
    };
    #endif // !__A1_H__// a2.h
    #ifndef __A2_H__
    #define __A2_H__
    //#include"a1.h"
    class A1;class A2
    {
    public:A1* pm;
    };
    #endif // !__A1_H__
  • 有些情況下需要類的完整定義而不是前向聲明

    • 在類A1的定義中加入類A2類型的對象
    • 在類A1的定義中需要知道類A2對象的大小
    • 在類A1中需要調(diào)用A2的成員函數(shù)
    #ifndef __A2_H__
    #define __A2_H__
    //#include"a1.h"
    class A1;class A2
    {
    public:A1* pm;A1 pm;  // error
    };
    #endif // !__A1_H__
  • 類A1與類A2之間的直接1依賴.一般是避免這種設(shè)計。而是通過引入一個新類,讓類A1和類A2都依賴這個新的類,從而打破兩個類的之間的直接依賴

  • 解決1:

    • 引入中間層
    • 在這里插入圖片描述
  • 解決2

    • 使用接口
    • 在這里插入圖片描述

引用計數(shù)基礎(chǔ)理論和實踐

1. shared_ptr 實現(xiàn)及string存儲簡單說明

1.1 shared_ptr智能指針實現(xiàn)簡單說明

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
#include<memory>
using namespace std;int main()
{shared_ptr<int> myp(new int(5));cout << "icount = " << myp.use_count() << endl;  // 1{shared_ptr<int> myp1(myp);cout << "icount = " << myp.use_count() << endl;// 2}shared_ptr<int> myp1(myp);cout << "icount = " << myp.use_count() << endl;// 2return 0;
}

在這里插入圖片描述

1.2 string類型字符串存儲方式的簡單說明

  • string類型字符串存儲方式的簡單說明

  • 貪婪拷貝

  • 寫時復制

  • 短字符優(yōu)化

  • 在VS2022中(貪婪拷貝)

    int main()
    {std::string str1("123");std::string str2 = str1;printf("str1的地址:%p\n", str1.c_str());printf("str2的地址:%p\n", str2.c_str());/*
    str1的地址:0000000C7398F920
    str2的地址:0000000C7398F960*/return 0;
    }
    

    在這里插入圖片描述

2. 通過copy-on-write方式實現(xiàn)的mystring類

在這里插入圖片描述
在這里插入圖片描述

2.1 骨架與計數(shù)設(shè)計

2.2 構(gòu)造函數(shù)

2.3 拷貝構(gòu)造函數(shù)

2.4 析構(gòu)函數(shù)

2.5 拷貝賦值運算符

2.6 外部加鎖,內(nèi)部加鎖,寫時復制

  • 外部加鎖:調(diào)用者負責,用調(diào)用者決定跨線程使用共享對象時的加鎖時機
  • 內(nèi)部加鎖:對象將所有對自己的訪問串行化,通過每個成員函數(shù)加鎖的方法來實現(xiàn),這樣就不會在多線程中共享該對象時進行外部加鎖了。

2.7 通過指針修改mystring所指字符串的內(nèi)容


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include<memory>class MyString
{
public:MyString(const char* tmp=""):pvalue(new stringvalue(tmp)){//point = tmp;}MyString(const MyString& tmp) :pvalue(tmp.pvalue)  // 拷貝構(gòu)造函數(shù){pvalue->refcount++;}MyString& operator=(const MyString& tmp)  // 拷貝賦值運算符重載{/*	if (&tmp == this)return *this;delete[] point;point = new char[strlen(tmp.point) + 1];strcpy(point,tmp.point);return *this;*/if (&tmp == this)return *this;//delete[] pvalue->point;--pvalue->refcount;//自己所指的引用計數(shù)減一if (pvalue->refcount == 0)delete pvalue; //把自己所指向的pvalue刪除pvalue = tmp.pvalue;pvalue->refcount++;return *this;}//const char& operator[](int idx)const   // 非const 可以與const版本共存,但是都存在時都會調(diào)用非const版本的//{//	return pvalue->point[idx];//}char& operator[](int idx)  // const []{if (pvalue->refcount > 1){--pvalue->refcount;pvalue = new stringvalue(pvalue->point); // 寫時復制}return pvalue->point[idx];}~MyString(){pvalue->refcount--;if (pvalue->refcount == 0)delete pvalue;}
private://char* point;struct stringvalue{size_t refcount; // 引用計數(shù)char* point;  stringvalue(const char* tmpstr){point = new char[strlen(tmpstr) + 1];strcpy(point,tmpstr);}~stringvalue(){delete[] point;}};
private:stringvalue* pvalue;
};
int main()
{return 0;
}
http://www.risenshineclean.com/news/58187.html

相關(guān)文章:

  • 如何查找做網(wǎng)站的服務商白山網(wǎng)絡推廣
  • 做網(wǎng)站跟app的區(qū)別營銷策劃公司排行榜
  • 自己做的網(wǎng)站怎么設(shè)置文件下載國外免費網(wǎng)站服務器
  • 怎樣做企業(yè)手機網(wǎng)站seo二級目錄
  • 網(wǎng)站點擊量怎么看關(guān)鍵詞排名批量查詢
  • 云客服系統(tǒng)合肥百度搜索優(yōu)化
  • 6黃頁網(wǎng)站建設(shè)網(wǎng)絡推廣公司主要做什么
  • 站點建錯了網(wǎng)頁能打開嗎seo還有用嗎
  • 網(wǎng)站html地圖怎么做百度廣告競價
  • 全椒做網(wǎng)站seo專業(yè)培訓
  • 網(wǎng)站建設(shè)內(nèi)容和功能的介紹做百度推廣銷售怎么找客戶
  • 中山比好的做網(wǎng)站的公司企業(yè)官網(wǎng)
  • 自己買主機可以做網(wǎng)站嗎濟南seo小黑seo
  • 建設(shè)網(wǎng)站公司價格2024年將爆發(fā)新瘟疫
  • 做網(wǎng)站的榮譽證書推廣運營公司哪家好
  • 專門做詳情頁的網(wǎng)站google海外版
  • 男女做那種的視頻網(wǎng)站百度平臺客服聯(lián)系方式
  • 馬鞍山建設(shè)機械網(wǎng)站seo優(yōu)化網(wǎng)站的注意事項
  • 手機免費制作ppt的軟件下載谷歌seo一個月費用需要2萬嗎
  • 佳木斯城鄉(xiāng)建設(shè)局官方網(wǎng)站seo排名專業(yè)公司
  • wordpress建站專家網(wǎng)絡營銷需要學什么
  • 建設(shè)網(wǎng)站的策劃書最強大的搜索引擎
  • 網(wǎng)站設(shè)計的原則有哪些互聯(lián)網(wǎng)營銷的方法
  • 軟件和網(wǎng)站開發(fā)seo專家是什么意思
  • 做網(wǎng)站ie緩存重慶seo團隊
  • 工廠做網(wǎng)站有用嗎廊坊seo外包
  • 定州網(wǎng)站制作營銷網(wǎng)站建設(shè)網(wǎng)站開發(fā)
  • dedecms 網(wǎng)站搬遷 模板路徑錯誤杭州網(wǎng)站推廣公司
  • 什么是平臺內(nèi)部營銷論壇seo設(shè)置
  • 功能分類模塊類型網(wǎng)站做整站優(yōu)化