Aracılığıyla paylaş


Satır içi işlevler (C++)

inline anahtar sözcüğü, derleyicinin bu işleve yapılan her çağrı yerine işlev tanımındaki kodu değiştirmesini önerir.

Teorik olarak, satır içi işlevlerin kullanılması, işlev çağrılarıyla ilişkili ek yükü ortadan kaldırdığı için programınızı hızlandırabilir. bir işlevi çağırmak için yığında dönüş adresini gönderme, bağımsız değişkenleri yığına gönderme, işlev gövdesine atlama ve işlev tamamlandığında bir dönüş yönergesi yürütme gerekir. Bu işlem, işlevin çizilmesiyle ortadan kalkar. Derleyici, satır içi genişletilmiş işlevleri iyileştirmeye yönelik farklı fırsatlara da sahiptir. Satır içi işlevlerin bir dezavantajı, programınızın genel boyutunun artabilmesidir.

Satır içi kod değişimi derleyicinin takdirine bağlı olarak yapılır. Örneğin, adresi alınırsa veya derleyici çok büyük olduğuna karar verirse, derleyici bir işlevi satır içi olarak satır içi yapmaz.

Sınıf bildiriminin gövdesinde tanımlanan bir işlev örtük olarak satır içi bir işlevdir.

Örnek

Aşağıdaki sınıf bildiriminde Account oluşturucu, sınıf bildiriminin gövdesinde tanımlandığı için satır içi bir işlevdir. üye işlevleri GetBalance, Depositve Withdraw tanımlarında belirtilir inline . anahtar inline sözcüğü, sınıf bildirimindeki işlev bildirimlerinde isteğe bağlıdır.

// account.h
class Account
{
public:
    Account(double initial_balance)
    {
        balance = initial_balance;
    }

    double GetBalance() const;
    double Deposit(double amount);
    double Withdraw(double amount);

private:
    double balance;
};

inline double Account::GetBalance() const
{
    return balance;
}

inline double Account::Deposit(double amount)
{
    balance += amount;
    return balance;
}

inline double Account::Withdraw(double amount)
{
    balance -= amount;
    return balance;
}

Not

Sınıf bildiriminde, işlevler anahtar sözcüğü olmadan inline bildirilmiştir. anahtar inline sözcüğü sınıf bildiriminde belirtilebilir; sonuç aynıdır.

Belirli bir satır içi üye işlevi, her derleme biriminde aynı şekilde bildirilmelidir. Satır içi işlevin tam olarak bir tanımı olmalıdır.

Bir sınıf üyesi işlevi, bu işlevin tanımı tanımlayıcıyı içermediği sürece varsayılan olarak dış bağlantı olarak inline kullanılır. Yukarıdaki örnekte, bu işlevleri belirticisi ile açıkça bildirmeniz gerekmeyen bir örnek gösterilmektedir inline . inline İşlev tanımında kullanılması, derleyiciye satır içi işlev olarak ele alındığını önerir. Ancak, bir işlevi bu işleve yapılan çağrıdan sonra olduğu gibi inline yeniden oluşturamazsınız.

inline, __inlineve __forceinline

inline ve __inline tanımlayıcıları derleyiciye işlevin çağrıldığı her yere işlev gövdesinin bir kopyasını eklemesini önerir.

Satır içi genişletme veya satır içi genişletme olarak adlandırılan ekleme, yalnızca derleyicinin kendi maliyet avantajı analizinin buna değer olduğunu göstermesi durumunda gerçekleşir. Satır içi genişletme, daha büyük kod boyutunun olası maliyetine göre işlev çağrısı ek yükünü en aza indirir.

anahtar __forceinline sözcüğü maliyet-fayda analizini geçersiz kılar ve bunun yerine programcının kararına dayanır. kullanırken __forceinlinedikkatli olun. 'nin ayırt edici olmayan kullanımı __forceinline , yalnızca marjinal performans kazançlarıyla daha büyük kodlara veya bazı durumlarda performans kayıplarına neden olabilir (örneğin, daha büyük bir yürütülebilir dosyanın disk belleğinin artması nedeniyle).

Derleyici, satır içi genişleme seçeneklerine ve anahtar sözcüklerine öneri olarak davranır. İşlevlerin çizili olacağının garantisi yoktur. Derleyiciyi, anahtar sözcüğüyle bile belirli bir işlevi satır içi olarak satır içi olarak çizmeye __forceinline zorlayamazsınız. ile /clrderleme yaptığınızda, işleve güvenlik öznitelikleri uygulanmışsa derleyici bir işlevi satır içi olarak satır içi yapmaz.

Derleyici seçeneği/Za (Dil uzantılarını devre dışı bırak) belirtilmediği sürece, önceki sürümlerle _inline uyumluluk için __inline ve _forceinline sırasıyla ve __forceinlineiçin eş anlamlılardır.

inline anahtar sözcüğü derleyiciye satır içi genişletmenin tercih edilir olduğunu söyler. Ancak, derleyici bunu yoksayabilir. Bu davranışın gerçekleşebileceği iki durum şunlardır:

  • Özyinelemeli işlevler.
  • Çeviri birimi içinde başka bir yerdeki işaretçi üzerinden başvurulan işlevler.

Bu nedenler, derleyici tarafından belirlendiği gibi diğerleri gibi, inlining'i etkileyebilir. Bir işlevin inline inlined olmasına neden olmak için tanımlayıcıya bağımlı olma.

Derleyici, üst bilgi dosyasında tanımlanan satır içi bir işlevi genişletmek yerine, bunu birden fazla çeviri biriminde çağrılabilir bir işlev olarak oluşturabilir. Derleyici, tek tanım kuralı (ODR) ihlallerini önlemek için bağlayıcı için oluşturulan işlevi işaretler.

Normal işlevlerde olduğu gibi, satır içi işlevde bağımsız değişken değerlendirmesi için tanımlı bir düzen yoktur. Aslında, normal işlev çağrısı protokolü kullanılarak geçirildiğinde bağımsız değişken değerlendirme sırasından farklı olabilir.

Satır içi işlev genişletmesinin /Ob gerçekten gerçekleşip gerçekleşmediğini etkilemek için derleyici iyileştirme seçeneğini kullanın.
/LTCG kaynak kodunda istenip istenmediğini belirten çapraz modüller arası inlining yapar.

Örnek 1

// inline_keyword1.cpp
// compile with: /c
inline int max(int a, int b)
{
    return a < b ? b : a;
}

Bir sınıfın üye işlevleri, anahtar sözcüğü kullanılarak veya işlev tanımını sınıf tanımına inline yerleştirerek satır içinde bildirilebilir.

Örnek 2

// inline_keyword2.cpp
// compile with: /EHsc /c
#include <iostream>

class MyClass
{
public:
    void print() { std::cout << i; }   // Implicitly inline

private:
    int i;
};

Microsoft'a özgü

__inline anahtar sözcüğü ile inlineeşdeğerdir.

ile __forceinlinebile, aşağıdaki durumlarda derleyici bir işlevi satır içi olarak satır içi olarak çizemez:

  • İşlev veya çağıranı ile /Ob0 derlenir (hata ayıklama derlemeleri için varsayılan seçenek).
  • İşlev ve çağıran farklı türde özel durum işlemelerini kullanır (birinde C++ özel durum işlemesi, diğerinde yapı özel durum işlemesi).
  • İşlevin değişken bağımsız değişken listesi vardır.
  • İşlev, , /O1veya /O2ile /Oxderlenemediği sürece satır içi derleme kullanır.
  • İşlev özyinelemeli ve ayarlanmadı #pragma inline_recursion(on) . Pragma ile özyinelemeli işlevler varsayılan 16 aramalı derinliğe kadar satır içi yapılırlar. Inlining derinliğini azaltmak için pragma kullanın inline_depth .
  • İşlev sanaldır ve sanal olarak çağrılır. Sanal işlevlere doğrudan çağrılar satır içi yapılabilir.
  • Program işlevin adresini alır ve çağrı işlev işaretçisi aracılığıyla yapılır. Adresleri alınmış olan doğrudan işlev çağrıları satır içi yapılabilir.
  • İşlev, değiştirici ile naked __declspec de işaretlenir.

Derleyici ile __forceinlinebildirilen bir işlevi satır içi olarak oluşturamıyorsa, aşağıdaki durumlar dışında bir düzey 1 uyarısı oluşturur:

  • İşlev , /Od veya /Ob0 kullanılarak derlenmiş. Bu gibi durumlarda hiçbir inlining beklenmiyor.
  • İşlev, dahil edilen bir kitaplıkta veya başka bir çeviri biriminde harici olarak tanımlanır ya da sanal çağrı hedefi veya dolaylı çağrı hedefidir. Derleyici, geçerli çeviri ünitesinde bulamayan, çizili olmayan kodu belirleyemez.

Özyinelemeli işlevler, en fazla 16 çağrı olmak üzere pragma tarafından inline_depth belirtilen derinliğe doğru satır içi kodla değiştirilebilir. Bu derinlikten sonra yinelenen işlev çağrıları, işlev örneği çağrıları olarak kabul edilir. Özyinelemeli işlevlerin satır içi buluşsal tarafından incelendiği derinlik 16'yi aşamaz. Pragma, inline_recursion şu anda genişletme aşamasında olan bir işlevin satır içi genişlemesini denetler. İlgili bilgiler için Satır İçi İşlev Genişletme (/Ob) derleyici seçeneğine bakın.

END Microsoft'a Özgü

Tanımlayıcıyı inline kullanma hakkında daha fazla bilgi için bkz:

Satır içi işlevlerin ne zaman kullanılacağı

Satır içi işlevler, veri üyelerine erişim sağlayanlar gibi küçük işlevler için en iyi şekilde kullanılır. Kısa işlevler, işlev çağrılarının ek yüküne duyarlıdır. Daha uzun işlevler çağrı ve dönüş sırasında orantılı olarak daha az zaman harcar ve daha az iç içe geçirme avantajı sağlar.

Bir Point sınıf aşağıdaki gibi tanımlanabilir:

// when_to_use_inline_functions.cpp
// compile with: /c
class Point
{
public:
    // Define "accessor" functions
    // as reference types.
    unsigned& x();
    unsigned& y();

private:
    unsigned _x;
    unsigned _y;
};

inline unsigned& Point::x()
{
    return _x;
}

inline unsigned& Point::y()
{
    return _y;
}

Koordinat işlemenin, böyle bir sınıfın istemcisinde görece yaygın bir işlem olduğu varsayıldığında, iki erişimci işlevi (x ve y önceki örnekte) inline belirtilmesi genellikle ek yükü şu şekilde kaydeder:

  • İşlev çağrıları (parametre geçirme ve nesnenin adresini yığına yerleştirme dahil)
  • Arayanın yığın çerçevesinin korunması
  • Yeni yığın çerçevesi kurulumu
  • Dönüş değeri iletişimi
  • Eski yığın çerçevesini geri yükleme
  • İade

Satır içi işlevler ile makrolar karşılaştırması

Makronun işlevle inline bazı ortak öğeleri vardır. Ancak önemli farklılıklar vardır. Aşağıdaki örneği inceleyin:

#include <iostream>

#define mult1(a, b) a * b
#define mult2(a, b) (a) * (b)
#define mult3(a, b) ((a) * (b))

inline int multiply(int a, int b)
{
    return a * b;
}

int main()
{
    std::cout << (48 / mult1(2 + 2, 3 + 3)) << std::endl; // outputs 33
    std::cout << (48 / mult2(2 + 2, 3 + 3)) << std::endl; // outputs 72
    std::cout << (48 / mult3(2 + 2, 3 + 3)) << std::endl; // outputs 2
    std::cout << (48 / multiply(2 + 2, 3 + 3)) << std::endl; // outputs 2

    std::cout << mult3(2, 2.2) << std::endl; // no warning
    std::cout << multiply(2, 2.2); // Warning C4244	'argument': conversion from 'double' to 'int', possible loss of data
}
33
72
2
2
4.4
4

Makro ile satır içi işlev arasındaki farklardan bazıları şunlardır:

  • Makrolar her zaman satır içinde genişletilir. Ancak, satır içi bir işlev yalnızca derleyici bunun yapılacak en uygun şey olduğunu belirlediğinde satır içi işlevin satır içine eklenir.
  • Makro beklenmeyen davranışlara neden olabilir ve bu da küçük hatalara yol açabilir. Örneğin, ifade mult1(2 + 2, 3 + 3) 11 olarak değerlendirilecek şekilde genişletilir 2 + 2 * 3 + 3 , ancak beklenen sonuç 24'dür. İşlev makrosunun #define mult2(a, b) (a) * (b)her iki bağımsız değişkeninin çevresine parantezler ekleyip eldeki sorunu çözecek ancak daha büyük bir ifadenin parçası olduğunda yine de şaşırtıcı davranışlara neden olabilen bir düzeltmedir. Bu, önceki örnekte gösterilmiştir ve makro gibi #define mult3(a, b) ((a) * (b))tanımlanarak sorun giderilebilir.
  • Satır içi bir işlev derleyici tarafından anlamsal işlemeye tabiyken, ön işlemci makroları aynı avantaj olmadan genişletir. Makrolar tür açısından güvenli değildir, ancak işlevler güvenlidir.
  • Satır içi işlevlere bağımsız değişken olarak geçirilen ifadeler bir kez değerlendirilir. Bazı durumlarda, makrolara bağımsız değişken olarak geçirilen ifadeler birden çok kez değerlendirilebilir. Örneğin, aşağıdakileri göz önünde bulundurun:
#include <iostream>

#define sqr(a) ((a) * (a))

int increment(int& number)
{
    return number++;
}

inline int square(int a)
{
    return a * a;
}

int main()
{
    int c = 5;
    std::cout << sqr(increment(c)) << std::endl; // outputs 30
    std::cout << c << std::endl; // outputs 7

    c = 5;
    std::cout << square(increment(c)) << std::endl; // outputs 25
    std::cout << c; // outputs 6
}
30
7
25
6

Bu örnekte, ifade sqr(increment(c)) olarak genişletildikçe işlev increment iki kez çağrılır((increment(c)) * (increment(c))). Bu, ikinci çağrısının increment 6 döndürmesine neden oldu, bu nedenle ifade 30 olarak değerlendirilir. Yan etkiler içeren herhangi bir ifade, makroda kullanıldığında sonucu etkileyebilir, davranışın amaçlandığını denetlemek için tamamen genişletilmiş makroyu inceleyin. Bunun yerine, satır içi işlev square kullanıldıysa işlev increment yalnızca bir kez çağrılır ve 25'in doğru sonucu elde edilir.

Ayrıca bkz.

noinline
auto_inline