Heim >Backend-Entwicklung >C#.Net-Tutorial >Denken in C++, Band 1, Lesenotizen, Schlüsselzusammenfassung

Denken in C++, Band 1, Lesenotizen, Schlüsselzusammenfassung

php是最好的语言
php是最好的语言Original
2018-07-28 11:43:281554Durchsuche

In diesem Artikel geht es hauptsächlich darum, einige Notizen aus dem ersten Band von Thinking in C++ zu lesen. Hauptsächlich einige Punkte, die es zu beachten gilt

  • Denken in C++ Kapitel 2

    • Übersetzer:

    • Compilerschritte zum Kompilieren des Programms:

    • Deklaration und Definition von Funktionen oder Variablen

    • Verbindung

  • Denken in C++ Kapitel 3

    • Funktionsrückgabebeschreibung

    • Üblich Funktionsbibliothek

    • Beschreibung der for-Schleife

    • Schalterbeschreibung

    • Bezeichner(Bezeichner)

    • void*-Zeigerbeschreibung

    • Gültiger Bereich der Variablen

    • Globale Variable

    • Statische Variablen

    • Verbindung (Verknüpfungstyp)

    • Vorverarbeitungsmakros

    • typedef-Syntaxbeschreibung

    • Enum-Beschreibung:

  • Denken in C++ Kapitel 5

    • Freunde

    • Verschachtelte Freunde

    • andere strukturieren

  • Mitdenken C++ Kapitel 6

    • Entsprechende Beschreibung des Konstruktors

    • lösche void*

    • Standardkonstruktor

  • Denken in C++ Kapitel 7

    • Überlastung

    • Typ- sichere Verknüpfung

    • Union

    • Standardparameterverwendungsregeln:

    • Platzhalterparameter

  • Denken in C++ Kapitel 8

    • const-Beschreibung

    • const-Zeiger

    • Zuordnung und Typprüfung

    • Temporäre Variable

    • Übergabe- und Rücksendeadresse

    • Standardparameterübergabe

    • Klasseninterne Datenelemente werden konstant initialisiert

    • Konstanten während der Kompilierung

    • const-Objekte und Mitgliedsfunktionen

  • Denken in C++ Kapitel 9

    • Inline-Funktionen

    • Verstecktes Verhalten von Konstruktoren und Destruktoren

    • Container vorverarbeiten

  • Denken in C++ Kapitel 10

    • statisches

    • statisches Objekt

    • interner Link

    • andere

    • stactic/const/stctic const

    • Statische Memberfunktion

  • Zeiger-Array/Array-Zeiger

  • Funktionszeiger

    • Funktionsadresse

    • Funktionszeiger

    • Funktionszeiger aufrufen

    • Array von Funktionszeigern

    • Typedef vereinfacht den Arbeitsaufwand

  • Namespace

    • Namespace

    • Unbenannter Namespace

    • Using-Direktive & Using-Deklaration

  • new/malloc/delete/free

    • new/ malloc

    • löschen/freigeben

    • Global neu überladen und löschen

    • Klasse überladen neu und löschen

    • Neu überladen und löschen für Arrays löschen

    • Neu positionieren/löschen

  • Vererbung und Zusammensetzung

    • Initialisierungsausdruck

    • Die Reihenfolge der Konstruktor- und Destruktoraufrufe

    • Nicht -Automatisch geerbte Funktionen

    • Vererbung und statische Mitgliedsfunktionen

    • Private Vererbung

    • Upcasting und Kopieren Konstruktor

  • Operatorüberladung

    • +=,-=,*=,/=,= und andere Operatorüberladung

    • Erklärung der Funktionsparameter und Rückgabewerte

    • Präfix ++ & Postfix ++

    • Konstanter Rückgabewert und Rückgabewert Optimieren

    • Operator[]

    • Operator-> (Zeiger-Dereferenzierungsoperator)

    • Operator->*

    • Grundlegende Richtlinien für Operator-Mitgliedsfunktionen

    • Copy-on-Write

    • Automatische Typkonvertierung

Denken in C++ Kapitel 2

Übersetzer:

  1. Interpreter

  2. Compiler

Compiler-Schritte zum Kompilieren eines Programms:

  1. Der Präprozessor verarbeitet Vorverarbeitungsanweisungen

  2. Der erste Durchgang analysiert den Vorverarbeitungscode, um die Anzahl der Knoten zu generieren, die durchgeführt wird, bevor mit dem zweiten Schritt fortgefahren wird Optimierer: Der Codegenerator im zweiten Durchgang analysiert den Codebaum, um Maschinensprache oder Assemblersprache zu generieren

Statische Typprüfung wird im ersten Durchgang durchgeführt

Deklaration und Definition von Funktionen oder Variablen

void function();//声明void function(){}//定义extern int a;//声明int a;//定义

Verbindung

In der letzten Phase des Kompilierungsprozesses wird das vom Compiler generierte Zielmodul mit einer ausführbaren Datei verbunden (kann sein). vom Betriebssystem erkannt )

Denken in C++ Kapitel 3

Funktionsrückgabebeschreibung

Die Return-Anweisung verlässt die Funktion und kehrt zum Punkt nach dem Funktionsaufruf, Lenovo Stack, zurück Betrieb

Normalerweise werden Funktionsbibliotheken

von einem Bibliotheksmanager verwaltet, der Objektmodule verwaltet. Dieser Bibliotheksmanager verwaltet .lib/.a-Dateien

Erklärung zu for-Schleifen

for(initialization;conditional;step)

Die for-Schleife führt zunächst die Initialisierung durch und bestimmt dann die Bedingung. Wenn sie erfüllt ist, tritt sie in die Schleife ein. Fahren Sie nach dem Ausführen der Schleife mit Schritt

Schalterbeschreibung

switch(selector){
    case integral-value:statement;break;    ...
    defualt:statement;
}

fort Der Selektor im Schalter muss ein ganzzahliger Wert sein, und der ganzzahlige Wert muss sein. Der ganzzahlige Wert

Selektor kann auch ein Wert vom Typ Enum sein

Der Spezifizierer (Spezifizierer ) ​​

wird verwendet, um die Bedeutung des integrierten Basistyps zu ändern und den Basistyp in einen größeren Satz zu konvertieren
1 short und long, das Schlüsselwort int kann weggelassen werden)
2. float/double: kein long float, nur long double , das heißt: float / double /long double
3 in Ganzzahl- und Zeichentypen)

void*-Zeigerbeschreibung

void*-Zeiger kann als beliebiger Adresstyp zugewiesen werden, aber eine falsche Typkonvertierung führt zum Absturz des Programms

Der effektive Gültigkeitsbereich der Variablen

Beginnt vom Definitionspunkt bis zum nächsten Nachbarn, bevor die Variable definiert wird. Die erste schließende Klammer des Paares offener Klammern ist der Gültigkeitsbereich bestimmt durch das letzte Klammerpaar, in dem sich die Variable befindet.

Globale Variablen

Der Lebenszyklus globaler Variablen dauert bis zum Ende des Programms. Sie können das Schlüsselwort extern verwenden, um globale Variablen in einer anderen Datei zu verwenden

Statisch (statisch). ) Variablen

Der Vorteil einer statischen Variablen besteht darin, dass sie nicht außerhalb des Funktionsbereichs verfügbar ist und nicht einfach geändert werden kann, wodurch Fehler lokalisiert werden. Wenn statische Variablen auf den Funktionsnamen und alle externen Variablen angewendet werden Funktion, es bedeutet „in der Datei Dieser Name kann nicht extern verwendet werden“, das heißt, es hat einen Dateibereich wie

//file1.cppstatic int fs;int main(){
    fs = 1;
}//file2.cppextern int fs;//编译器不会找到file1.cpp文件中的fs,即文件作用域void function(){
    fs = 100;
}
extern static int i;int main(){    cout << i << endl;
}static int i = 0;
将出现error,即全局静态变量声明是有文件作用域的,编译器将会产生错误

Verbindung (Verknüpfungstyp)

  1. interne Verbindung: Nur die zu kompilierende Datei wird gespeichert. Erstellen Sie einen separaten Speicherplatz für jede Kennung. Die interne Verbindung wird durch das Schlüsselwort static

  2. angegeben. Externe Verbindung: Erstellen Sie einen separaten Speicherplatz für alle kompilierten Dateien, d. h. alle Variablen und Funktionen sind in diesem Bereich enthalten

    Automatische (lokale) Variablen existieren nur vorübergehend auf dem Stapel, der Linker kennt keine automatischen Variablen, daher sind diese Variablen sind nicht verbunden

Vorverarbeitungsmakros

#define PRINT(STR,VAR) \
    cout << STR << VAR << endl; \
    cout << VAR << STR <<endl

即可以像调用函数一样调用PRINT,这里预处理宏分行使用&#39;\&#39;,宏只是展开,并替换

#define PRINT(STR,VAR) \
    cout << #STR << VAR << endl

这里&#39;#&#39;表示STR字符串化(stringizing),比如:int i = 0;PRINT(i,i); //这里输出应该是 i:0

typedef-Syntaxbeschreibung

typedef existent-type-description alias-name

  1. typedef unsinged long ulong;

  2. typedef int* intPtr

  3. typedef struct MyStruct{
    //Hier ist das C reguläre Strukturdefinition
    } MyStruct;

  4. typedef int (*fun)(int,int) //Der Funktionszeiger-Alias ​​macht Spaß

Enum-Beschreibung:

Die Prüfung der Enum in C++ ist strenger (a ist eine Farbtyp-Enumeration) in C, aber nicht in C++, da a++ zunächst zwei Konvertierungen durchführt. Der Farbtyp wird in int konvertiert und dann um 1 erhöht. Konvertiert den Wert in Farbe und ist bei der zweiten Konvertierung unzulässig

Thinking in C++ Chapter 5

Friends

Kann über das Schlüsselwort „friend“ aufgerufen werden. Interne private Mitgliedsvariablen oder Mitgliedsfunktionen

struct X;struct Y{    void f(X*);
};struct X{    private:    int i;    public:    void initialize();    friend void g(X*,int); //Global friend
    friend void Y::f(X*); //struct member friend
    friend struct z;    //Entire struct is a friend
    friend void h();
}

Y::f(X*) beziehen sich auf die Adresse eines X Der Compiler weiß, wie eine Adresse übergeben wird. Unabhängig davon, welches Objekt übergeben wird, hat die Adresse eine feste Größe. Wenn er versucht, das gesamte Objekt zu übergeben, muss der Compiler die gesamte Definition von X kennen, um dessen Größe zu bestimmen und wie es übergeben wird , unter Verwendung einer unvollständigen Typspezifikation (unvollständige Typspezifikation), d. h. Deklaration von Struktur Sie können die folgende Methode verwenden, um auf die

Deklaration einer verschachtelten Set-Struktur

  1. zuzugreifen, die angibt, dass die Struktur ein weltweit verwendeter Freund ist

  2. definiert die Struktur

  3. const in sz = 20;struct Holder{    private:    int a[sz];    public:    void initialize();    struct Pointer;
        friend Pointer;    struct Pointer{    private:
        Holder* h;    int* p;    public:    void initialize(Holder* h);    void next();    void previous();    void top();    void end();    int read();    void set(int i);
        };
    };void Holder::initialize(){
        memset(a,0,sz*sizeof(int));
    }void Holder::Pointer::initialize(Holder* rv){
        h = rv;
        p = rv->a;
    }
    ...int main(){
        Hodler h;
        Holder::Pointer hp;    int i;
        h.initialize();
        hp.initialize(&h);
        ...
    }
  4. struct other

    //: Hadler.h
    class Handler{
        struct Cheshire;
        Cheshire* smile;
        public:    ...};
    //:~
    //:Handler.cpp
    struct Handler::Cheshire{
        int i;    ...}...

  5. Die Struktur Cheshire in der Handler.h-Datei ist eine unvollständige Typbeschreibung oder Klassendeklaration, und die spezifische Klassendefinition wird in der Implementierungsdatei abgelegt

Denken in C++ Kapitel 6

Entsprechende Beschreibung des Konstruktors

class Object{public:    Object(int number = 0);private:    int m_number;
};//:~//: mainint main(int argc, char *argv[])
{    int i = 0;    switch(i){    case 0:        Object obj1{1};        break;    case 1:            //error: cannot jump from switch statement to this case label "case 1:"
        Object obj2{2};   //jump bypasses variable initialization "Object obj1{1};"
        break;
    }    return 0;
}

Der obige Code meldet einen Fehler

Switch überspringt den Sequenzpunkt des Konstruktors. Auch wenn der Konstruktor nicht aufgerufen wird, funktioniert dieses Objekt auch im nachfolgenden Programmblock und es tritt hier ein Fehler auf

soll sicherstellen, dass das Objekt gleichzeitig mit seiner Erstellung initialisiert wird. goto wird auch solche Fehler erzeugen.

void* löschen

Wenn void* auf ein nicht integriertes Objekt zeigt, wird nur der Speicher freigegeben und der Destruktor wird nicht ausgeführt

默认构造函数

class Object{public:    Object(int number);private:    int m_number;
};Object object[2] = {Object{1}};

Object没有默认构造函数,数组声明初始化时将报错,object[1]必须有默认构造函数进行初始化,否则报错当且仅当没有构造函数时编译器会自动创建一个默认构造函数

Thinking in C++ Chapter 7

overload

使用范围和参数可以进行重载

void f();class X{void f();};

类型安全连接(type-safe linkage)

1.cppvoid functin(int);2.cppvoid function(char);int main(){    function(1); //cause a linker error;
    return 0;
}

编译成功,在C中连接成功,但是在C++中连接出错,这是C++中的一种机制:类型安全连接

Union

class SuperVar{    enum{
    character,
    integer,
    floating_point
    } vartype;    union{    char c;    int i;    float f;
    };    public:
    SuperVal(char ch);
    SuperVal(int ii);
    SuperVal(float ff);    void print();
};

SuperVal::SuperVali(char ch){
    vartype = character;
    c = ch;
}
SuperVal::SuperVali(int ii){
    vartype = integer;
    i = ii;
}
SuperVal::SuperVali(float ff){
    vartype = floating_type;
    f = ff;
}void SuperVal::print(){    switch(vartype){    case character:    cout << "character:" << c <<endl;    break;    case integer:    cout << "integer :" << i <<endl;    break;    case floating_point:    cout << "float :" << f <<endl;    break;
    }
}int main(){
    SuperVar A(&#39;c&#39;),B(12),C(1.44f);
    A.print();
    B.print();
    C.print();    return 0;
}
  1. enum没有类型名,因为后面没有必要涉及美剧的类型名称,所以枚举类型名可选,非必须

  2. union没有类型名和标识符,称为匿名联合(anonymous union),不需要使用标识符和以点操作符方式访问这个union的元素

  3. 访问一个匿名联合成员就像访问普通变量一样,唯一区别在于:该联合的两个变量占用同一内存空间,如果匿名union在文件作用域内(在所有函数和类之外),则它必须声明为static,以使它有内部的连接

默认参数使用规则:

  1. 只有参数列表的后部参数才可以是默认的

  2. 一旦在一个函数调用中开始使用默认参数,那么这个参数后面的所有参数都必须为默认的

占位符参数

void f(int i, int = 0, float = 1.1); //version 1void f(int i ,int , float flt); // version 2

其中version 2除了i,flt之外中间参数就是占位符参数

Thinking in C++ Chapter 8

const说明

C++中const默认为内部连接,仅在const被定义的文件中才可见,在连接时不能被其它编译单元看见,当定义一个const时必须赋值给它,除非使用extern进行说明

extern const int bufsize;

通常c++不为const创建空间,将其定义保存在符号表内,但是上面的extern进行了强制内存空间分配,另外如取const的地址也是需要存储空间的分配。

对于复杂的结构,编译器建立存储,阻止常量折叠。在C中const默认为外部连接,C++默认为内部连接.出现在所有函数外部的const作用域是整个文件,默认为内部连接

const指针

  1. const修饰指针正指向的对象 const int* a;

  2. const修饰在指针中的地址  int* const a;

赋值和类型检查

  1. const对象地址不可以赋值给一个非const指针,但是可以吧一个非const对象地址赋值给一个const指针

  2. 字符数据的字面值:

char * cp = "howdy";char cp[] = "howdy";

指针cp指向一个常量值,即常量字符数组,数组cp的写法允许对howdy进行修改

临时变量

在求表达式值期间,编译器必须创建零时变量,编译器为所有的临时变量自动生成为const

传递和返回地址

void t(int*) {}void u(const int* clip){  //*clip = 2; error
  int i = *clip;  //int * ip2 = clip error;}const char* v(){  return "result of functin 0";
}const int * const w(){  static int i;  return &i;
}int main(){  int x= 0;  int * ip = &x;  const int * cip = &x;
  t(ip); //ok
  //t(cip);  not ok;
  u(ip); //ok
  u(cip);//ok
  //char * cp = v(); not ok
  const char* ccp = v();//ok
  //int * ip2 = w(); not ok
  const int * const ccip = w();// ok
  const int* cip2 = w();//ok
  //*w() = 1; not ok}
  1. const指针不可以赋值给非const指针,但是非const指针可以赋值给const指针

  2. 函数v()返回一个从字符数组的字面值中建立的const char *,在编译器建立了它并把它存储在静态存储区之后,该声明实际上产生该字符数组的字面值的地址

  3. 函数w()返回值要求这个指针以及这个指针所指向的对象均为常量,与函数v()类似,因为i是静态的,所以函数返回后返回值仍然有效

  4. const int* const w()只有在作左值时第二个const才能显现作用,所以w()返回值可以赋值给const int *

标准参数传递

可以将临时对象传递给const引用,但不能将一个临时对象传递给接收指针的函数,对于指针必须明确接受地址(临时变量总是const)

class X
{public:    X() {}
};
X f(){return X();}void g1(X&){ }void g2(const X&){ }int main(int argc, char *argv[])
{
    g1(f()); //error!
    g2(f());    return 0;
}

类内数据成员为const初始化

必须在构造函数的初始化列表中进行初始化

编译期间的常量

  1. 一个内建类型的static const可以看成编译期间的常量,但是该static const必须在定义的地方进行初始化

  2. 无标记enum也可以看成为编译期间常量,一个枚举在编译期间必须有值

class X{
  enum {size = 1000};  //same as static const
  static const int size = 1000;  int i[size];
}

const对象和成员函数

  1. 若将一个成员函数声明为const,则该成员函数可以被const对象调用

  2. const成员函数可以调用非const成员和const成员,非const成员函数同样可以使用const成员

  3. const对象只能调用const成员函数,非const对象调用非const成员函数

Thinking in C++ Chapter 9

内联函数

内联函数与普通函数一样执行,但是内联函数在适当的地方像宏一样展开,不需要函数调用的开销(压栈,出栈),任何在类内部定义的函数自动成为内联函数

  1. 内联函数体过大时,编译器将放弃使用内联

  2. 当取函数地址时,编译器也将放弃内联

  3. 一个内联函数在类中向前引用一个还没有声明的函数时,是可以的,因为C++规定只有在类声明结束后,其中的内联函数才会被计算

class Forward{    int i;    public:    Forward():i(0){}    int f() const {return g()+i;}    int g() const {return i;}
}

构造函数和析构函数隐藏行为

class X
{    int i,j,k;public:
    X(int x = 0):i(x),j(x),k(x) { cout << "X" <<endl;}   //X(int x):i(x),j(x),k(x){cout << "X" <<endl;}
    ~X(){cout << "~X" <<endl;}
};class Y{
    X q,r,s;    int i;public:
    Y(int ii):i(ii){cout << "Y" <<endl;}
    ~Y(){        cout << "~Y" <<endl;
    }
};int main(int argc, char *argv[])
{
    Y y(1);    return 0;
}//:~//:outputX
X
X
Y
~Y
~X
~X
~X
  1. 类中包含子对象,构造函数先调用子对象的构造函数,如果没有默认构造函数,则必须在初始化列表中进行初始化,然后在调用类的构造函数

  2. 类中包含子对象,先调用类的析构函数在调用子类的析构函数

预处理器

1. #define DEBUG(x) cout << #x "=" << x<<endl;#:字符串化,详见REF2. #define FIELD(a) char* a##_string;int a##_size##:标志粘贴,允许设两个标识符并将它们粘贴在一起产生新的标识符

Thinking in C++ Chapter 10

static

  1. 在固定地址上进行存储分配,在一个特殊的静态数据区上创建,不是在堆栈上产生

  2. 对一个特定编译单位来说是局部的,static可以控制名字的可见性,该名字在这个单元或者类以外是不可见的

static对象

  1. 如果没有为一个内建类型的静态变量提供一个初始化值,编译器会确保在程序开始时它被初始化为零(转化成适当的类型)

  2. 如果在定义一个静态对象时没有指定构造参数时,该类必须有默认的构造函数

内部链接

常量、内联函数默认情况下为内部链接

others

  1. 所有全局对象隐含为静态存储

  2. 对static函数意味着只在本单元可见,成为文件静态(file static)

stactic/const/stctic const

  1. static成员必须在类外初始化,如果不初始化,则编译器不会进行默认初始化,对于非内建类型,可以使用构造函数初始化代替“=”操作符

  2. 类内const成员必须在构造函数的初始化列表中进行初始化

  3. static const变量(内建类型)必须在声明的地方就初始化

  4. static对象数组,包括const和非const数组必须在类外部初始化

  5. 自定义class类声明为stctic,不管其为const或者非const都必须在类外初始化

  6. 类的静态成员必须进行初始化后才可以使用

静态成员函数

  1. 类的静态成员函数不能访问一般数据成员或者函数,只能访问静态数据成员,也能调用其他静态成员函数

  2. 静态成员函数没有this指针

指针数组/数组指针

优先级低的先读
*p[] 指针数组
(*p)[] 数组指针,即指向一个数组

函数指针

函数地址(Function Address)

函数的地址:函数名后不跟参数

void fun(){}fun即为函数地址fun()为函数的调用

函数指针

double pam(int); //prototypedouble (*pf)(int); //function pointerpf=pam;//pf now points to the pam();

调用函数指针

double x=pf(5);double x=(*pf)(5);

函数指针数组

const double* f1(const double ar[],int n);const double* f2(const double [],int);const double* f3(coanst double *,int);//f1,f2,f3函数声明本质一样const double* (*pa[3])(const double * , int) = {f1,f2,f3};  //[]优先级高于*,所以表示pa是个数组,数组中包含三个指针auto pb = pa;const double * px = pa[0](av,3);const double * py = (*pb[1])(av,3);double x = *pa[0](av,3);double y = *(pb[1])(av,3);

指向整个数组的指针,即是一个指针,而不是一个数组,优先级低的先读
*p[] 指针数组
(*p)[] 数组指针,即指向一个数组

const double*  (*(*pd)[3])(const double* , int) = &pa;->等价形式 auto pd = &pa;

pd指向数组,*pd就是数组,而(*pd)[i]是数组中的元素,即函数指针
函数调用:

(*pd)[i](av,3)->此处返回const double *
*(*pd)[i](av,3)->此处返回 double
另外一种函数调用略复杂(*(*pd)[i])(av,3)->返回const double *
*(*(*pd)[i])(av,3)->返回 double

typedef简化工作量

typedef const double* (*p_fun)(const double* ,int);
p_fun p1 = f1;
p_fun pa[3] = {f1,f2,f3};
p_fun (*pd)[3] = &pa;

namespace

namespace

  1. namespace只能在全局范围内定义,但是可以相互嵌套

  2. 在namespace定义的结尾,右花括号后面不必跟一个分号

  3. 可以按类的语法来定义一个namespace,定义的内容可以在多个头文件中延续,就好像重复定义这个namespace

//:header1.h
namespace MyLib{
    extern int x;
    void f();
    //...}
//:~
//:header2.h
namespace MyLib{
    extern int y;
    void g();
    //...}
  1. 一个namespace的名字可以用另一个名字来作为它的别名
    namespace lib = MyLib;

  2. 不能像类一样创建一个名字空间的实例

未命名的名字空间

namespace {
    class A{};
    class B{};
    int i,j,k;
    //...}

将局部名字放在一个未命名的名字空间中,不需要加上static就可以作为内部连接

using directive& using declaration

using directive:using namespace xxusing declaration:using xx::f;

new/malloc/delete/free

new/malloc

new计算内存大小,并调用构造函数,malloc需要手工计算内存大小,不调用构造函数

delete/free

delete先执行析构函数,在清空内存,free直接清空内存
delete用于void*时将不会调用析构函数,直接清空内存

重载全局new和delete

  1. 重载的new必须有一个size_t参数,该参数由编译器产生并传递给我们,分配内存的长度,必须返回一个指向等于该长度的对象的指针,如果没有找到存储单元,则返回一个0,然而如果找不到存储单元,不能仅仅返回0,还应该有new-handler或产生一个异常信息

  2. new返回void*,而不是指向任何特定类型的指针,只需要完成内存分配,而不是完成对象的创建,直到构造函数调用才能完成对象的创建,调用构造函数是编译器完成的

  3. delete参数是由new分配的void*指针,该参数是在调用析构函数后得到的指针,析构函数从存储单元中移去对象

#include <cstdio>#include <cstdlib>using namespace std;void* operator new(size_t sz){    printf("operator new:%d Bytes\n",sz);    void* m = malloc(sz);    if(!m) puts("out of memry");    return m;
}void operator delete(void* m){    puts("operator delete");    free(m);
}class S{    int i[100];public:
    S(){puts("S::S()");}
    ~S(){puts("S::~S()");}
};int main(int argc, char *argv[])
{    int * p = new int(47);    delete p;
    S* s = new S;    delete s;
    S* sa = new S[3];    delete [] sa;    return 0;
}//:outputoperator new:4 Bytesoperator deleteoperator new:400 Bytes
S::S()
S::~S()operator deleteoperator new:1208 Bytes
S::S()
S::S()
S::S()
S::~S()
S::~S()
S::~S()operator delete

这里使用printf(),puts()等函数,而不是iostreams,因为使用iostreams对象时(全局对象cin,cout,cerr),调用new分配内存,printf不会进入死锁状态,它不调用new来初始化自身

类重载new和delete

//p328

主要思想:使用static数组以及一个bool数组,返回static数组的下标地址,进行new,得到static下标数组进行delete
void* operator new(size_t) throw(bad_alloc);
void operator delete(void*);

为数组重载new和delete

//p331

定位new/delete

//p333

主要思想:使用new运算符重载,但是不对delete运算符重载,指定内存位置new
void* operator new(size_t,void*);

继承与组合(Inheritance&Composition)

初始化表达式

  1. 成员对象如果没有默认的构造函数,必须显示的进行初始化,即在构造函数初始化列表中进行初始化

  2. 只有执行成员类型初始化之后,才会进入构造函数

  3. 没有对所有成员以及基类对象的构造函数调用之前,若基类没有默认构造函数则必须初始化,即在初始化列表中进行初始化,否则无法进入构造函数体

构造函数和析构函数调用的顺序

  1. 构造函数调用的顺序:首先调用基类构造函数,然后调用成员对象的构造函数,调用成员对象构造函数的顺序是按照成员对象在类中声明的顺序执行,最后调用自己的构造函数

  2. 析构函数调用次序与构造函数调用次序相反,先调用自己的析构函数,在调用成员函数的析构函数,最后调用基类析构函数

  3. 对于多重继承,构造函数调用顺序为继承时的声明顺序

非自动继承的函数

  1. 构造函数

  2. 析构函数

  3. operator=

继承与静态成员函数

静态成员函数与非静态成员函数的共同特点:
1. 均可以被继承到派生类中
2. 重新定义一个静态成员,所有基类中的其他重载函数会被隐藏
3. 如果我们改变了基类中的函数的特征,所有使用该函数名字的基类版本将会被隐藏。
4. 静态成员函数不可以是虚函数

私有继承

  1. 使用私有继承是为了不允许该对象的处理像一个基类对象,一般private更适合于组合

  2. 私有继承成员公有化

class Base{    public:    Base(){}
    ~Base(){}    void name() {cout << "Base name" <<endl;}
}
class Derived:private Base{    public:    Derived(){}
    ~Derived(){}    using Base::name; //私有继承公有化}

向上类型转换和拷贝构造函数

class Base{    public:    Base(){}
    Base(const Base&){}
    ~Base(){}    void name() {cout << "Base name" <<endl;}
}
class Derived:public Base{    public:    Derived(){}
    Derived(const Derived& d):Base(d){ }//这里是调用基类的拷贝构造函数
    ~Derived(){}
}

基类拷贝构造函数的调用将一个Derived引用向上类型转换成一个Base引用,并且使用它来执行拷贝构造函数,向上类型转换是安全的

运算符重载

+=,-=,*=,/=,=等一类运算符重载

首先进行自我检查(是否对自身赋值),即this == &val,在进行运算,“=”运算符只允许作为成员函数进行重载

函数参数和返回值说明

  1. 对于任何函数参数,如果仅需要从参数中读而不改变它,默认地应当做const引用传递。普通算数运算符(像“+”,“-”)和bool运算符不会改变参数,所以const引用为主要传递方式,当函数时成员函数时,就转换为const成员函数。只有会改变左侧参数的运算符赋值(如+=)和operator=,左侧参数不是常量,但因参数将被改变,所以参数仍然按地址传递

  2. 返回值类型取决于运算符的具体含义,如果使用该运算符产生一个新值,就需要产生一个作为返回对象的新对象。例如operator+必须生成一个操作数之和的对象,该对象作为一个常量通过返回值返回,所以作为一个左值不会被改变

  3. 所有赋值运算符均改变左值,为了使赋值结果能用于链式表达式(如a=b=c),应该能够返回一个刚刚改变了的左值的引用。但该引用并非一定要是常量引用,如(a=b).f(),这里b赋值给a,a调用成员函数f,因此所有赋值运算符的返回值对于左值应该是非常量引用(如果是常量引用,则f成员函数必须为const函数,否则无法调用该函数,这与愿望相违背)

  4. 对于逻辑运算符,人们至少希望得到一个int返回值,或者最好是bool值

Prefix ++ & Postfix ++

  1. 成员函数

const Object& operator++(){}const Object operator++(int){}
  1. 友元函数

const Object& operator++(Object& obj){}const Object operator++(Object& obj,int){}

对于友元函数重载来说,因为传入的Object对象被改变,所以使用非常量引用
 前缀通过引用返回,后缀通过值(临时对象)返回,因为后缀返回临时对象,所以后缀通过常量值返回,前缀返回引用,如果希望可以继续改变对象则返回引用,否则通过常量引用返回比较合适,这样与后缀保持了一致性

常量传值返回与返回值优化

  1. 作为常量通常通过传值方式返回。考虑二元运算符+,假设在表达式f(a+b)中使用,a+b的结果变为一个临时对象(Object),该对象被f()调用,因为它为临时的,所以自动被定义为常量,所以无论是否返回值为常量都没有关系。但是如果使用(a+b).f(),这里设返回值为常量规定了对于返回值只有常量成员函数才可以调用

  2. 返回值优化通过传值方式返回要创建的的新对象时,注意使用的形式,如operator+

version 1:return Object(lObj.i+rObj.i);
version 2:
Object tmp(lObj.i+rObj.i);return tmp;

version 2将会发生三件事,首先创建tmp对象,然后调用拷贝构造函数把tmp拷贝到外部返回值的存储单元中,最后当tmp在作用域的结尾时调用析构函数
 version 1编译器直接将该对象创建在外部返回值的内存单元,不是整的创建一个局部变量所以仅需要一个普通的构造函数调用(不需要拷贝构造函数),且不会调用析构函数,效率高。这种方式被称为返回值优化。

operator[]

该运算符必须是成员函数,而且只接受一个参数,可以返回一个引用,可以用于等号左侧。

operator->(指针间接引用运算符)

该运算符一定是一个成员函数,它必须返回一个对象(或者引用),该对象也有一个指针间接引用运算符;或者必须返回一个指针,被用于选择指针间接引用运算符箭头所指的内容

class Obj{    static int i,j;    public:    void f() const {cout<<i++<<endl;}    void g() const {cout<<j++<<endl;}
};int Obj::i = 47;int Obj::j = 11;class ObjContainer{    vector<obj* >a;    public:    void add(Obj* obj) {a.push_back(obj);}    friend class SmartPointer;
};class SmartPointer{
    ObjContainer& oc;    int index;    public:
    SmartPointer(ObjContainer & obj):oc(obj){
    index = 0;
    }    bool operator++(){    if(index >= oc.a.size()) return false;    if(oc.a[++index] == 0) return false;    return true;
    }    bool operator++(int){    return operator++();
    }
    Obj* operator->() const{    return oc.a[index];
    }
};

指针间接引用运算符自动的为用SmartPointer::operator->返回的Obj*调用成员函数

class Obj{    static int i,j;    public:    void f() const {cout<<i++<<endl;}    void g() const {cout<<j++<<endl;}
};int Obj::i = 47;int Obj::j = 11;class ObjContainer{    vector<obj* >a;    public:    void add(Obj* obj) {a.push_back(obj);}    class SmartPointer; //声明友元之前必须告知该类存在
    friend SmartPointer;    class SmartPointer{
        ObjContainer& oc;        int index;    public:
    SmartPointer(ObjContainer & obj):oc(obj){
        index = 0;
    }    bool operator++(){        if(index >= oc.a.size()) return false;        if(oc.a[++index] == 0) return false;        return true;
    }    bool operator++(int){        return operator++();
    }
    Obj* operator->() const{        return oc.a[index];
    }
    };
    SmartPointer begin(){    return SmartPointer(*this);
    }
};

operator->*

//p294

运算符成员函数基本方针

  1. 所有一元运算符建议为成员

  2. =()[]->->*必须为成员

  3. += -= /= *= ^= &= |= %= >>= <<=建议为成员

  4. 所有其他二元运算符为非成员

copy-on-write

//p301引用计数

自动类型转换

  • 构造函数转换:构造函数能把另外一个类型对象(或者引用)作为它的单个参数,该构造函数允许编译器执行自动类型转换

  • 运算符转换:运算符重载,创建一个成员函数,该函数通过关键字operator后跟随想要转换的类型的方法,将当前类型转换为希望的类型,自动类型转换只发生在函数调用值中,而不在成员选择期间

class Three{    int i;    public:    Three(int ii = 0,int = 0):i(ii){}
};
class Four{    int x;    public:    Four(int xx):x(xx){}    operator Three() const {return Three(x);}
};void g(Three){}int main(){
    Four four(1);
    g(four);
    g(1);
}
  • 二义性错误

class Orange;
class Apple{    public:    operator Orange() const;
};
class Orange{    public:    Orange(Apple);
};void f(Orange){}int main(){
    Apple a;    // f(a);  error:二义性错误}
  • 扇出错误:提供不止一种类型的自动转换

class Orange{};class Pear{};class Apple{
    public:
    operator Orange() const;
    operator Pear() const;
};void eat(Orange);void eat(Apple);int main(){
    Apple a;    //eat(a); error:扇出错误}

相关文章:

【读书笔记】精通CSS 第二版

《深入理解bootstrap》读书笔记:第一章 入门准备

相关视频:

李炎恢PHP视频教程第一季

Das obige ist der detaillierte Inhalt vonDenken in C++, Band 1, Lesenotizen, Schlüsselzusammenfassung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn