2011-05-22 70 views
2

來自Java背景,我試圖學習如何以最簡單的方式處理C/C++中的內存(分配)分配。在C/C++中使用堆棧進行內存管理時的編碼風格

一位同事建議我只爲成員變量分配內存,並讓棧處理局部變量。我不完全知道這是什麼概念被稱爲,但它意味着功能將這樣進行:

void inc(int x, int &y){ 
    y=x+1; 
} 

另一種方法是這樣的:

int inc(int x, int &y){ 
    y=x+1; 
    return y; 
} 

第一個禁止我使用它在表達式,即:

int y; 
inc(2,y); 
inc(y,y); 

第二個呢,但它並不漂亮:

int y; 
y=inc(inc(2,y),y); 

在我搞砸我的代碼之前,經驗豐富的C/C++程序員會考慮這種編碼風格嗎?

+0

如果你一次只增加一個,爲什麼不用'operator ++'代替? – 2011-05-22 14:28:38

+0

@Rafe:哪段代碼是「錯誤的」?我只問,因爲[都是](http://codepad.org/YS0FVZGw)[非常好](http://codepad.org/GvMyNc2D)。 – 2011-05-22 14:31:01

+0

我認爲他的同事指的是RAII風格的C++,而不是通過ref傳遞POD類型。基於堆棧的POD類型不需要「內存管理」。 – user7116 2011-05-22 14:31:58

回答

1

這不太可能是您的同事所指的編程風格。像整數或簡單結構等POD類型不是您通常關心的數據。 Resource Acquisition is Initialization或RAII是C++中的一種常用策略,它利用棧分配變量的屬性,從而保證它們的析構函數在大多數情況下被調用。

仿皮RAII代碼:

// take a reference to some resource 'r' 
void frob(resource& r, int val) 
{ 
    other_resource or(val); 

    or << r; // use of r requires no pointer manipulation, etc 
} // 'or' is destructed at the end of 'frob' 
    // even in exceptional situations. 

int main (int argc, char argv[][]) 
{ 
    resource r(1, "a", 3.0); 

    frob(r, 9); 

    return 0; // after this 'r' will be destructed 
} 
5

我將使用該功能嚴重阻礙

int inc(int x, int &y) { 
    y=x+1; 
    return y; 
} 

給程序員,爲什麼函數修改的輸入,並返回值目前還不清楚,他們都是相同的對象


真的,在我看來,選擇是間:

// #1 
void inc(int x, int& y) { 
    y=x+1; 
} 

int y = 0; 
inc(2, y); 

// #2 
int inc(int x) { 
    return x+1; 
} 

int y = inc(2); 

在一般情況下,我還是比較喜歡#2,因爲我覺得 「out參數」 古風笨重的使用。正如你指出的那樣,你最終會在表達式中掙扎,並且當調用函數時,實際發生的事情並不十分清楚。如果你有一個比int更復雜的對象(比如說一個數組,或者一個大類,或者你只是想「返回」多個對象),它可能會讓對象更容易處理如果你不在函數內部創建任何新對象,那麼製作#1是更方便的選擇。

我想我試圖在這裏畫出的結論是它取決於場景。試圖概括這些事情是愚蠢的錯誤。


- 使用指針,而不是引用解決了一些,但它確實引入膨脹與現在不必費心檢查無效指針:

// #3 
void inc(int x, int* y) { 
    assert(y); // at least, we can check that it's not NULL 
    *y = x+1; 
} 

int y = 0; 
inc(2, &y); // clear here that I'm passing a pointer 
+0

[我不會打擾NULL檢查,但那只是我。](http://stackoverflow.com/questions/4390007/in-either-c-or-c-should -i-check-pointer-parameters-for-null) – 2011-05-22 16:23:30

+0

@Billy:\ *點頭\ *儘管你不能否認你仍然留下一些更容易出錯的東西。 – 2011-05-22 16:44:15

3

還有第三個更簡單的方法:

int inc(int x) { 
    return x+1; 
} 

int y = inc(inc(2)); 
0

你的問題與內存分配無關,只是按值或通過引用傳遞參數。

該函數傳遞參數y作爲參考,x參數正在被複制。

void inc(int x, int &y){ 
    y=x+1; 
} 

我個人認爲這種形式可能是嚴重頭痛的根源,因爲語法不會偏離價值傳遞。你應該避免它,除非你正在處理對象。我建議這種形式:

int inc(int x){ 
    return x+1; 
} 

y=inc(2) 
0

我認爲在第二個例子中,你不想y參考,所以不是

int inc(int x, int &y) 

你應該有

int inc(int x, int y) 

因爲當您在功能中編輯y時,它會再次編輯原始的y,而不僅僅是本地副本,這不是您要做的。

1

對於原語類型這是確定:

int inc(int x) { 
    return x+1; 
} 

用於更復雜的類型這樣做是爲了避免額外的複製當函數返回

void reverse_vector(const std::vector<int>& v, std::vector<int>* result) { 
    if (!result) return; 
    *result = v; 
    std::reverse(result->begin(), result->end(); 
} 
// ... 
std::vector<int> v; 
std::vector<int> reversed; 
reverse_vector(v, &reversed); 

對於我建議使用升壓堆上分配對象:: shared_ptr的( tr1 :: shared_ptr)庫。然後你可以編寫幾乎和你在java中一樣的代碼。

#include <string> 
#include <boost/shared_ptr.hpp> 
#include <boost/make_shared.hpp> 

class A { 
public: 
    A(int x, const std::string& str) 
     : x(x), str(str) { 
    } 

    void foo() { 
    } 
private: 
    int x; 
    const std::string& str; 
}; 

// ... 

boost::shared_ptr<A> a = boost::make_shared<A>(1, "hello"); 
a->foo(); 

您可以將boost :: shared_ptr對象視爲java引用。沒有垃圾收集(只是引用計數),所以你必須關心自己的週期。

請記住,shared_ptr比標準指針稍慢。

另外重要的是要記住,您應該避免複製大對象。這是更好,除非你需要海峽的副本FOO寫的

void foo(const std::string& str); 

代替

void foo(std::string str); 

還有一件事是編譯器很聰明,會爲你做一些優化。例如reverse_vector可以寫成

std::vector<int> reverse_vector(std::vector<int> v) { // note copying! 
    std::reverse(v.begin(), v.end()); 
    return v; // no additional copying of temporary due to RVO 
} 

這靜脈阻塞(返回值優化)是非常有用的,但有時編譯器不能自動執行。這就是爲什麼我會建議在不依賴RVO的情況下編寫這類函數,除非您知道它何時失敗。