C++ 入門 - Day7_ 関数の探索

1. C++ インライン関数

インライン関数は、プログラムの実行速度を向上させるために C++ によって行われた改良です。通常の関数とインライン関数の主な違いは、その記述方法ではなく、C++ コンパイラーがそれらをプログラムに組み込む方法です。通常の関数との違いを知るには、プログラムを深く理解する必要があります。

1: 従来機能の具体的な手順は以下のとおりです。

1-コンパイル プロセスの最終生成物は、一連の機械語命令で構成される実行可能プログラムです。プログラムが実行されると、オペレーティング システムはこれらの命令をコンピュータのメモリにロードして、各命令が特定のメモリ アドレスを持つようにし、コンピュータは命令を次々に実行します。

2-場合によっては (ループや分岐ステートメントなど) 一部の命令がスキップされ、特定のアドレスに前方または後方にジャンプします。

3-通常の関数呼び出しでも、プログラムは別のアドレス (関数のアドレス) にジャンプし、関数が終了すると戻ります。

4-関数呼び出し命令が実行されると、プログラムは関数呼び出し直後の命令のメモリ アドレスを保存し、関数パラメータをスタック (このために予約されているメモリ ブロック) にコピーし、その命令をマークするメモリ ユニットにジャンプします。関数の開始点を指定し、関数コードを実行します(戻り値をレジスタに入れる場合もあります)。

5-アドレスが保存されている命令に戻ります (これは、記事を読んで脚注を見るために立ち止まるのと似ており、読み終わった後に前の読書場所に戻るのと似ています)。前後にジャンプしてジャンプ位置を記録するということは、以前の機能を使用するときに一定のオーバーヘッドが発生することを意味します

 ※10分でご飯を食べに行くのに往復で2時間かかるようなので、非常に非効率です。

2: インライン関数の手順は次のとおりです。

1- インライン関数のコンパイル済みコードは、他のプログラム コードとともに「インライン化」されます。コンパイラは、関数呼び出しを対応する関数コードに置き換えます。

2- インライン コードの場合、プログラムはコードを実行するために別の場所にジャンプし、その後ジャンプして戻る必要がありません。

3- インライン関数は通常の関数よりもわずかに高速に実行されますが、より多くのメモリ領域を消費します。

どの機能を選択するべきか

関数の実行処理時間 > 呼び出し時間の場合、定期的な時間がより考慮されます。

関数の実行プロセス < 呼び出し時間の場合、インライン化がより考慮されます。

つまり、呼び出しに時間がかかり、関数のサイズが比較的小さい場合は、インライン関数をより考慮する必要があります。

3: インライン関数を使用する場合の対策は以下の通りです

関数宣言の前にinlineを追加します

関数定義の前にinline を追加します。

*通常は、すべての関数ヘッダーと関数コードを、プロトタイプが最初に提供された場所に直接配置します。

インライン関数 squar() (パラメーターの 2 乗を計算) によるインライン関数のデモについては、以下の例を参照してください。

#include <iostream>

using namespace std;

inline double square(double x){return x * x;}//简简单单的内联

int main(void)
{
    double a,b;
    double c = 13.0;

    a = square(5.0);
    b = square(4.5+7.5);

    cout << "a= " << a << ", b= " << b << endl;
    cout << "c= " << c << endl;
    cout << "Now c = " << square(c++) << endl;

    return 0;
}

結果:

a= 25、b= 144
c= 13
現在、c = 169

インラインとマクロ:

inline は C++ によって提供される新しい機能です。C 言語は、プリプロセッサ ステートメント #define を使用して、インライン コードの元の実装であるマクロを提供します。

たとえば、二乗を計算する次のマクロ

#define SQUARE(x) X*X;

これはパラメータの受け渡しではなく、テキストの置換によって実現されます。X は「パラメータ」の記号マーカーです。

マクロは値で渡すことはできませんが、インライン関数は値で渡されます

2. 参照変数

C++ は、準拠する型参照変数を追加しました。参照は、すでに定義されている変数の別名です。たとえば、twain を clement 変数への参照として使用する場合、twain と clement を同じ意味で使用して、その変数を表すことができます。

エイリアスの役割は何ですか?

主な目的は、関数の仮パラメータとして使用することです。

参照変数をパラメータとして使用すると、関数はコピーではなく元のデータを使用します。

1: 参照変数を作成する

例えば:

int ラット。

int & げっ歯類 = ラット;

このうち、& はアドレス演算子ではなく、型識別子の一部であり、宣言内の char * が char へのポインターを参照し、int & が int への参照を参照するのと同じです。

このようにして、げっ歯類とネズミは交換可能です。これらは同じ値とメモリの場所を指します。

例えば:

#include <iostream>

using namespace std;


int main(void)
{
   int rats = 10;
   int &rodents = rats;

   cout << "rats = " << rats << endl;
   cout << "rrodents = " << rodents << endl;//10

    rodents++;
    cout << "rats = " << rats << endl;//11
    cout << "rrodents = " << rodents << endl;

    cout << "&rats = " << &rats << endl;
    cout << "&rodents = " << &rodents << endl;

    return 0;
}

結果は次のとおりです。

ラット = 10
げっ歯類 = 10
ラット = 11
げっ歯類 = 11
&ラット = 0x80d1ff844
&げっ歯類 = 0x80d1ff844

*参照は、最初に宣言してからポインタのように割り当てるのではなく、宣言時に初期化する必要があります。

int ラット;

int &rodent;

げっ歯類 = ネズミ。

これは受け入れられません!

参照は実際にはポインタよりも const ポインタに近いです。参照は作成時に初期化する必要があります。一度変数に関連付けられると、常にその変数に忠実になります。

int &rodnets = ラット;

上記は実際には、次のコードを偽装して表現したものです。

int *const pr = &rats;

次のコードを見てください。
 

#include <iostream>

using namespace std;


int main(void)
{
    int rats = 101;
    int &rodents = rats;

    cout << "rats = " << rats << endl;
    cout << "rrodents = " << rodents << endl;//10

    cout << "&rats = " << &rats << endl;
    cout << "&rodents = " << &rodents << endl;

    int bunnies = 50;
    rodents = bunnies;

    cout << "bunnies = " << bunnies << endl;
    cout << "dodents = " << rodents << endl;//50
    cout << "rats = " << rats << endl;//50

    cout << "&bunnies = " << &bunnies << endl;

    return 0;
}

結果:

ラット = 101
げっ歯類 = 101
&ラット = 0xa4cdfff644
&げっ歯類 = 0xa4cdfff644
ウサギ = 50
ネズミ = 50
ラット = 50
&ウサギ = 0xa4cdfff640

*

1) ラットとげっ歯類のアドレスと値は常に同じであるため、常に関連付けられます。

2) バニーは単に 50 を割り当てるだけで、アドレスが異なるため関連性がありません。

2: 参照を関数の引数として使用する

次の例を使用して、参照の使用とポインタの使用を比較します。次に、3 つの方法を示します。

#include <iostream>

using namespace std;
void swapr(int &a, int &b);
void swapp(int *pa,int *pb);
void swapv(int a ,int b);

int main(void)
{
    int wallet1 = 300;
    int wallet2 = 350;

    cout << "Wallet1 = " << wallet1 << endl;
    cout << "Wallet2 = " << wallet2 << endl;

    cout << "Using reference to swap contents:" << endl;
    swapr(wallet1,wallet2);

    cout << "Wallet1 = " << wallet1 << endl;//350
    cout << "Wallet2 = " << wallet2 << endl;//300

    cout << "Using pointers to swap contents: " << endl;
    swapp(&wallet1,&wallet2);
    cout << "Wallet1 = " << wallet1 << endl;//300
    cout << "Wallet2 = " << wallet2 << endl;//350

    cout << "Tring to use passing by value:" << endl;
    swapv(wallet1,wallet2);
    cout << "wallet1 = " << wallet1 << endl;//300
    cout << "wallet2 = " << wallet2 << endl;//350

    return 0;
}
void swapr(int &a, int &b)//按引用传递值,其实此时&a和wallet1关联了,b和wallet2关联
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}
void swapp(int *pa,int *pb)//按指针传递值
{
    int temp;
    temp = *pa;
    *pa = *pb;
    *pb = temp;
}
void swapv(int a ,int b)//按值传递不可以用了
{
    int temp;

    temp = a;
    a = b;
    b = temp;
}

結果:

Wallet1 = 300
Wallet2 = 350
参照を使用して内容を交換する:
Wallet1 = 350
Wallet2 = 300
ポインタを使用して内容を交換する:
Wallet1 = 300
Wallet2 = 350
値による受け渡しを使用する:
Wallet1 = 300
Wallet2 = 350

* したがって、参照メソッドとポインター メソッドの両方がコンテンツを交換しますが、値による受け渡しは失敗することがわかります。

参照渡しでは a と b はwallet1とwallet2のエイリアスであり、値渡しでは変数abはwallet1とwallet2の新しい変数をコピーするだけなので、abを交換してもwallet1とwallet2の値は変更されません。

3: 文献の属性と特徴

次のコードを見てください。

#include <iostream>

using namespace std;
double cube(double x);
double recube(double &ra);

int main(void)
{
    double x = 3.0;
    cout << cube(x) << " = cube of " << x << endl;

    cout << recube(x) << " = cube of " << x << endl;//27 = cube of 27,出现了问题,


    return 0;
}
double cube(double a)//使用传统方法
{
    a *= a * a;
    return a;
}
double recube(double &ra)//使用引用方法,并不能改变x的值了
{
    ra *= ra * ra;
    return ra;
}

結果:

27 = 3の3乗
27 = 27の3乗

* ここで、ra は常に x に等しいため、変化しません。

ra * ra * ra を返すように変更するだけです。

結果は 27 = 3 の 3 乗になります。

一時変数、参照パラメータ、const:

実際の参照パラメータが一致しない場合、C++ は一時変数を生成します。現在、C++ では、パラメーターが const 参照である場合にのみこれが許可されます。

ここで、5.0 は定数です。 cout << recube2(5.0) << " = cube of " << "5.0" << endl;

また、double recube2(const double &ra) の場合、パラメータは参照変数です。

このとき、コンパイラは 5.0 を一時変数に渡し、それを参照に関連付けます。

cout << recube2(5.0+x)も可能

次に、refcube() 関数を見てみましょう。この関数の目的は、渡された値を変更することではなく、単に使用することであるため、一時変数は害を及ぼすことはなく、関数が処理できる引数の種類の点で関数をより一般的にします。

したがって、宣言参照を const として指定すると、C++ は必要に応じて一時変数を生成します。

** 参照変数パラメータは可能な限り const を使用する必要があります

const を使用して、データを誤って変更するプログラミング エラーを回避します。

関数が const 引数と非 const 引数を処理できるようにするために const を使用します。それ以外の場合は、非 const データのみを受け入れます。

const 参照を使用すると、関数が一時変数を正しく生成して使用できるようになります。

4: 構造体に参照を使用する

次の構造体定義のように、参照は構造体とクラスに最適です。

#include <iostream>
#include <string>

using namespace std;

struct free_throws
{
    string name;
    int made;
    int attempts;
    float percent;
};

int main(void)
{
    double x = 3.0;

    return 0;
}

その後機能します

プロトタイプは、構造体への参照を関数の引数として受け取り、次のように記述できます。

void Set_pc(free_throws & ft);

関数が渡された構造を変更したくない場合は、次のようにする必要があります。

void Set_pc(const free_throws & ft);

次のコードを見てください。

#include <iostream>
#include <string>

using namespace std;

struct free_throws
{
    string name;
    int made;
    int attempts;
    float percent;
};
void set_pc(free_throws &ft);
void display(const free_throws &ft);
free_throws &accumulate(free_throws &target, const free_throws &source);

int main(void)
{
    free_throws one = {"Rick",13,14};
    free_throws two = {"Jack",10,16};
    free_throws three = {"Jerry",7,9};
    free_throws four = {"jason",5,9};
    free_throws five = {"Michael",6,14};
    free_throws team = {"Class 6",0,0};

    free_throws dup;

    set_pc(one);
    display(one);
    accumulate(team,one);
    display(team);

    display(accumulate(team,two));//这里要注意display需要使用accumulate的返回值,所以必须给accumulate设置返回值
    display(accumulate(accumulate(team,three),four));//accumulate的返回值是结构体引用,也是他的形参,还缺个four形参,再套上display即可

    dup = accumulate(team,five);//新建的dup
    cout << "Display team:" << endl;
    display (team);
    display (dup);

    return 0;
}
void set_pc(free_throws &ft)//引用结构体类型
{
    if(ft.attempts != 0)
        ft.percent = 100 * float(ft.made) / float(ft.attempts);
    else
        ft.attempts = 0;
}
void display(const free_throws &ft)
{
    cout << "Name: " << ft.name << endl;
    cout << "Made: " << ft.made << endl;
    cout << "Attempts: " << ft.attempts << endl;
    cout << "Percent: " << ft.percent << endl << endl;
}
free_throws &accumulate(free_throws &target, const free_throws &source)//改编成有返回值的了
{
    target.attempts += source.attempts;
    target.made += source.made;
    set_pc(target);
    return target;
}

結果は次のとおりです。

名前: Rick
達成: 13
試行: 14
パーセント: 92.8571

名前: クラス 6
達成: 13
試行: 14
パーセント: 92.8571

名前: クラス 6
達成: 23
試行: 30
パーセント: 76.6667

名前: クラス 6
達成数: 35
試行数: 48
パーセント: 72.9167

表示チーム:
名前: クラス 6
成功: 41
試行数: 62
パーセント: 66.129

名前: クラス 6
達成数: 41
試行数: 62
パーセント: 66.129

*

  表示(累積(チーム,2));

//ここで、display は、accumulate の戻り値を使用する必要があるため、accumulate の戻り値を設定する必要があることに注意してください。
    display(accumulate(accumulate(team,three),four));

//accumulate の戻り値は構造体参照であり、これはその仮パラメータでもあり、まだ 4 つの仮パラメータが欠落しているため、それを表示できます

/*

2) 参照を返したいのはなぜですか?

3) 参照を返す際に注意が必要な問題

*/

5: クラスオブジェクトの参照を使用する

クラス オブジェクトを関数に渡すとき、C++ は通常、参照を使用します。文字列クラスを使用する次のコードを参照してください。

#include <iostream>
#include <string>

using namespace std;
string version1(const string &s1,const string &s2);
const string &version2(string &s1, const string &S2);
const string &version3(string &s1, const string &s2);

int main(void)
{   
    string input;
    string result;
    string copy;

    cout << "Enter a string: ";
    getline(cin,input);
    copy = input;
   
    cout << "Your string as entered: " << input << endl;
    
    result = version1(input,"***");//把输入加上前缀后缀
    cout << "Your string enhanced: " << result << endl;
    cout << "Your original string: " << input << endl;

    cout << "---------------------------------" << endl;

    result = version2(input,"###");
    cout << "Your string enhanced: " << result << endl;//rusult是version返回的结果,就是s1,s1和input关联
    cout << "Your original string: " << input << endl;//这个input和s1引用,应该一样

    cout << "---------------------------------" << endl;

    input = copy;
    result = version3(input,"@@@");
    cout << "Your string enhanced: " << result << endl;
    cout << "Your original string: " << input << endl;



    return 0;
}
string version1(const string &s1,const string &s2)//有const即使不匹配也会创建变量
{
    string temp;
    temp = s2 + s1 + s2;
    return temp;
}
const string &version2(string &s1, const string &s2)//返回string类引用
{
    s1 = s2 + s1 + s2;//着跟第一个的temp还是不一样的,temp是临时变量,最后就消失了
    return s1;
}
const string &version3(string &s1, const string &s2)
{
    string temp;//temp用完了就没了,引用没法返回了
    temp = s2 + s1 + s2;
    return temp;
}

結果:

文字列を入力してください: aaa
入力された文字列: aaa
拡張された文字列: ***aaa***
元の文字列: aaa
----------------------- ----------
強化された文字列: ###aaa###
元の文字列: ###aaa###
------------------ ---------------

version3 が直接クラッシュして終了しました

※version3のtempは使い切ると消滅し、参照は返せません

6: オブジェクト、継承、参照

ostream クラスと ofstream クラスに注目してください。これらには興味深いプロパティがあります。ofstream オブジェクトは ostream クラスのメソッドを使用できます。これにより、ファイル I/O 形式がコンソール I/O と同じになり、機能を渡すことが可能になります。あるクラスから別のクラスへ クラスの言語機能は継承と呼ばれます

継承のもう 1 つの特徴は、必須の型変換を行わずに、基本クラス参照が派生クラス オブジェクトを指すことができることです。

つまり、基本クラス参照をパラメーターとして受け入れる関数を定義できます。この関数を呼び出すときは、基本クラス オブジェクトをパラメーターとして使用することも、派生クラス オブジェクトをパラメーターとして使用することもできます。

次の例を参照してください。基本クラスと派生クラスのオブジェクトを使用します。

#include <iostream>
#include <fstream>
#include <cstdlib>

using namespace std;
const int LIMIT = 5;
void file_it(ostream &os,double fo,const double fe[],int n);

int main(void)
{   
    ofstream fout;//用子类fstream创建的对象
    const char *fn = "ep-data.txt";

    fout.open(fn);
    if(!fout.is_open())//打开失败
    {
        cout << "Can't open " << fn << ". Bye!" << endl;
        exit(EXIT_FAILURE);
    }
    double objective;
    cout << "Enter the focal Length of your telescope objective in mm: ";
    cin >> objective;

    double eps[LIMIT];
    for (int i = 0; i < LIMIT; i++)
    {
        cout << "EyePices" << i+1 << ": ";
        cin >> eps[i];
    }
    
    file_it(cout,objective,eps,LIMIT);//cout是父类iostream中的方法
    file_it(fout,objective,eps,LIMIT);//fout是子类创建的对象

    cout << "Done!" << endl;

    return 0;
}
void file_it(ostream &os,double fo,const double fe[],int n)//基类的引用可以指向基类对象和派生类对象
{
    os << "Focal length of objective: " << fo << endl;
    os << "f.1. eyepieces" << " magnification" << endl;
    for(int i = 0; i < n;i++)
    {
        os << "     " << fe[i] << " " << int(fo/fe[i] + 0.5) << endl;
    }
}

コンソールの結果:

望遠鏡の対物レンズの焦点距離を mm 単位で入力します: 1800
EyePices1: 30
EyePices2: 19
EyePices3: 21
EyePices4: 7.6
EyePices5: 12
対物レンズの焦点距離: 1800
f.1。接眼レンズの倍率
     30 60
     19 95
     21 86
     7.6 237
     12 150
完了しました。

テキスト結果:

 

*

void file_it(ostream &os,double fo,const double fe[],int n)

関数の最初のパラメーターは理解するのがさらに難しく、関数を呼び出すときに cout オブジェクトと fout サブクラス オブジェクトの両方を使用できるように、ostream の参照オブジェクトである基本クラスが使用されます。

7:参照パラメータを使用する場合

参照パラメータを使用する理由は 2 つあります。

プログラマは関数の呼び出しでデータ オブジェクトを変更できます

データオブジェクト全体ではなく参照を渡すことでプログラムの速度を向上させることができます

パラメータ参照は実際にはポインタベースのコードの別の構造であるため、データ オブジェクトが比較的大きい場合 (構造体やクラスのオブジェクトなど)、参照とポインタがより重要になります。

では、いつポインターを使用するのか、いつ参照を使用するのか、いつ値転送を使用するのか?

1) 渡された値を変更しない関数

組み込みデータ構造や小さな構造など、データ量が比較的小さい場合は値で渡します。

· データ オブジェクトが配列の場合、それが唯一のオプションであるためポインタを使用し、そのポインタを const へのポインタとして宣言します。

データ オブジェクトが大きな構造体の場合は、const ポインターまたは参照を使用してプログラムの効率を向上させます。これにより、構造体のコピーに必要な時間とスペースを節約できます。

· データ オブジェクトがクラス オブジェクトの場合は、const 参照を使用します。

2) 呼び出し関数内のデータを変更する関数の場合

· データ オブジェクトが組み込みデータ構造の場合は、ポインタを使用します。fixit(&x) のようなコードが表示された場合は、X を変更してください

・データオブジェクトが配列の場合はポインタのみ使用可能

· データ オブジェクトが構造体の場合は、参照またはポインタを使用します。

オブジェクト構造がクラスの場合は参照を使用する

3. デフォルトパラメータ

手順は次のとおりです。

#include <iostream>

using namespace std;
const int ArSize = 80;
char *left(const char *str,int n = 1);//在这里设置好默认参数

int main(void)
{   
    char sample[ArSize];
    cout << "Enter a string: " << endl;
    cin.get(sample,ArSize);

    char *ps = left(sample,4);//该函数提出4个字符
    cout << ps << endl;
    delete []ps;

    ps = left(sample);//不设置第二个参数,使用默认参数!
    cout << ps << endl;

    return 0;
}
char *left(const char *str,int n)
{
    if(n < 0)
        n=0;
    char *p = new char[n+1];//开辟了n+1的空间
    int i;
    for(i = 0; i < n && str[i]; i++)//具体能使用i个,且str[i]有效,就是str没越界
        p[i] = str[i];
    while(i <= n)//剩余的设置为空字符
        p[i++] = '\0';
    return p;
}

結果:

文字列を入力してください:
hello
hell
h

4. 関数のオーバーロード

* 左辺値、右辺値

    int a = 10; //a就是左值,10就是右值
    int b =20;//b是左值,20是右值

    int c = a + b;//此时a+b是右值
    a + b = c;//这样就不行

アドレスを取得できるものは左辺値、アドレスを取得できないものは右辺値です。

たとえば、右側の 10 と 20 はレジスタに格納されており、そこからアドレスを取得することはできません。

* 左辺値参照、右辺値参照

int a = 10; //a就是左值,10就是右值
    int b =20;//b是左值,20是右值

    int &c = a;//a是左值,所以这个引用时左值引用
    int &d = 10;//这样就不行
    int &d = (a+b);//这样也不行

    const int &d = 10;
    const int &c = (a+b);//加上const就可以实现右值引用,其实是和临时变量产生关系

定数参照では、d および c を介して値を変更することはできません

したがって、データを変更するために、右辺値参照 && が導入されます。

int &&x = 10;//これは右辺値参照です

int &&y = (a+b);

関数多態性は、C をベースにした C++ の新しい関数です。デフォルトの引数を使用すると、異なる数の引数を使用して同じ関数を呼び出すことができます

関数のポリモーフィズム (オーバーロード) により、同じ名前の複数の関数を使用できるようになります。通常、オーバーロードという名前を使用します。

たとえば、ミス・ピギーは野球場で故郷のチームを応援したり(根)、野原に植物を植えたり(根)、文脈に応じてさまざまな状況で根の意味を判断したりできます。C++ は、コンテキストを通じてオーバーロードされた関数のバージョンも決定します。

*関数のオーバーロードの鍵は、関数のパラメーター リスト (関数シグネチャとも呼ばれます) です。2 つの関数のパラメータの数と型が同じで、パラメータ リストの順序も同じであれば、それらのシグネチャは同じであり、変数名は重要ではありません。

C++ では、シグネチャが異なる限り、同じ名前の関数を定義できます。

例えば:
 

void print (const char * str, int width);
void print(double d,int width);
void print(long ;,int width);
void print(int i,int width);
void print(const char *str);

コンパイラは採用される用途に応じて署名のプロトタイプに対応します

Supongo que te gusta

Origin blog.csdn.net/leikang111/article/details/125320954
Recomendado
Clasificación