2010-05-24 72 views
1

我的任務如下: 創建一個具有char * name和int age類的Person。使用動態分配內存的變量,析構函數,函數init和好友函數show來實現構造器。然後將此類轉換爲頭文件和cpp文件並在其他程序中實現。好了,所以這裏是我的Person類:Cpp一些基本問題

#include <iostream> 
using namespace std; 

class Person { 
    char* name; 
    int age; 
public: 

    Person(){ 
     int size=0; 
     cout << "Give length of char*" << endl; 
     cin >> size; 
     name = new char[size];  
     age = 0; 
    } 

    Person::~Person(){ 
     cout << "Destroying resources" << endl; 
     delete [] name; 
     delete take_age(); 
    } 

    friend void show(Person &p); 

    int* take_age(){ 
     return &age; 
    } 

    char* take_name(){ 
     return name;  
    } 

    void init(char* n, int a) { 
     name = n; 
     age = a; 
    } 
}; 

void show(Person *p){ 
    cout << "Name: " << p->take_name() << "," << "age: " << p->take_age() << endl; 
} 

int main(void) { 
    Person *p = new Person; 
    p->init("Mary", 25); 

    show(p); 

    system("PAUSE"); 
    return 0; 
} 

現在通過頁眉/實現部分:
- 我需要在頭/執行文件介紹構造?如果是 - 如何?
- 我的show()函數是一個友好的函數。我應該以某種方式考慮它嗎?

我已經沒有我的考試返回此任務,但我仍想知道如何實現它。

+3

如果你不已經有一個,你應該考慮讓初學者C++在[權威的C++書指南和列表]上市叢書之一(http://stackoverflow.com/questions/388242/the -definitive-C-書指南和列表)。 – 2010-05-24 23:29:27

+1

對不起,如果聽起來很刺耳,但你的代碼真的顯示,你完全不知道發生了什麼。這只是一個事實。你真的需要讀些東西。 – 2010-05-24 23:35:41

+0

C++不叫cpp。 C預處理器被稱爲cpp。 – hobbs 2010-05-24 23:52:20

回答

5

解決很多問題的,通過切換從char *std::string。你會很高興你做到了。

std::string類負責內存分配的,和釋放以及複製。

如果這是作業,請說服您的教授使用std::string爲初學者和保存char *的指針部分。同時提醒您的教授,C++語言與C語言不同。這是其中的一個領域。

+0

不同意。任何C++程序員都應該知道如何使用char *字符串以及std :: string實例。 C++是比C複雜得多的語言,但它們都有相同的基礎,任何C++程序員都應該知道它們。 – ebasconp 2010-05-25 00:52:42

+0

@ebasconp:他們是否應該知道這是一個不同的問題,因爲它是否適合這個作業問題。所以我發現自己同意托馬斯。 – 2010-05-25 15:27:01

+0

+1不需要強制用戶在C++代碼中使用'char *',除非與需要API的接口連接。它使事情變得複雜,並留下內存泄漏/管理不善的空間。 – 2010-05-25 15:49:26

4

使用deletedelete[]時,不需要*。只需提供一個指針變量,例如。

delete[] name; 

此外,您take_age成員聲稱返回int*但實際上你返回int成員本身。如果你想這樣做,你需要使用&的成員地址。由於@傑裏評論說這是而不是你想在這裏做什麼。

+2

...或將其返回類型更改爲int。返回一個指向內部對象的指針是你通常應該避免的東西(在這種情況下,似乎沒有理由這麼做)。 – 2010-05-24 23:37:34

+0

@Jerry:絕對如此。 – Troubadour 2010-05-24 23:40:09

2

我想你應該調查下面的代碼片段(如,什麼是他們腳下,這裏發生了什麼,等...)

int * take_age(); // You should return plain `int` here, I assume 


~Person(){ 
    cout << "Destroying resources" << endl; 
    delete *[] name; // Do you understand why did you put `*` here? 
    delete * take_age(); // Do you understand why did you write this? What behaviour you were trying to achieve? 

而且,實際上,等等。只有當你完成了basic的東西,我想,你可以轉到頭設計問題和朋友功能。

3

雖然這個網站上的一些人顯然認爲這是完全可以接受的,但是最好的做法(請參閱Can a constructor return a NULL value?),您應該避免在對象的構造函數中進行流操作等操作。在外面讀取數據流,然後用結果調用函數。

也就是說,恕我直言,你應該採取的第一步。

+0

充分尊重,這至多是不相干的,更糟糕​​的是完全錯誤。 OP的問題來自於只有初學者對該語言的瞭解,而不受你的教條建議的幫助。有一個反序列化的構造函數是完全正確的,即使你個人不喜歡它們。事實上,對於不可變的對象來說,它實際上是實現序列化的唯一方法。 – 2010-05-25 15:29:59

+0

是的,我想如果他們想出去找一份工作,這只是一個很好的建議。如果他們想成爲一個體面的業餘愛好開發人員,這也是一個很好的建議。如果他們想要的只是一個黑客攻擊,如果在構造函數中調用控制檯流來完成工作...... – 2010-05-25 15:39:32

+0

不,對任何人都不是好的建議。我沒有看到任何合理的論據支持你的結論,即施工人員必須儘量減少他們的行爲,以及許多好的論點。由於某種宗教信仰而任意限制自己不是成爲黑客的替代品,而是成爲一名合格專業人士的替代選擇。 – 2010-05-26 19:49:39

0

我認爲你的問題是這條線: 朋友無效(人& p); 它需要什麼。

我需要在頭/執行文件介紹構造? 構造函數可以位於.h或.cpp文件中。沒關係。通常如果函數很短,可以將其包含在.h文件中。任何更長的時間應該在.cpp中。

我的show()函數是一個友好的函數。 不確定這是什麼意思。朋友函數存在於類定義之外。你的演出功能是在課堂上定義的,所以不需要成爲朋友。

3

在一個典型的情況下,管理一個指針和動態分配的內存塊(例如本例中的名稱)對於一個類是足夠的責任。因此,托馬斯馬修斯是正確的:在這種情況下你應該真的使用字符串。如果你要自己處理它,你仍然應該把這個職責分解成它自己的一個類,然後把這個類的一個對象嵌入到你的Person對象中。如果有的話,std::string已經嘗試做太多;你會更好,而不是更少。

您的刪除應該與您的分配完全匹配。在這種情況下,唯一的分配是:

name = new char[size];  

所以才刪除應該是:

delete [] name; 

至於friend功能去的,通常要在類定義內friend聲明,但功能定義在類定義之外:

class Person { 
// ... 
    friend void show(Person const &); 
// ... 
}; 

void show(Person const &p) { 
    // ... 
} 

還有其他的可能性,但這是一般的想法。特別是,朋友從來不是會員功能。你所擁有的是一個名爲show的單一全局函數的聲明和一個完全獨立的成員函數的定義 - 它碰巧具有相同的名稱,但根本不是完全相同的函數。

這表明另外一點:const正確性。您將參數作爲參考傳遞給Person。除非要修改Person對象(在這種情況下,show()看起來像名稱的一個糟糕的選擇),它可能應該引用一個const對象。同樣的總體思路適用於take_age() - 因爲它只檢索一個值,它應該是一個const功能:

int take_age() const { return age; } 

我可能已經試圖掩蓋太多了,所以我會閉嘴的時刻...

1

首先,試圖找到正確的方式來實現你的班級,特別是在已經錯過了答案後的榮譽。

從你頂部的描述中,我想你可能誤解了一些被要求完成這項任務的人。首先,我的解釋是設置名稱和年齡的值應該在init()函數中進行,而不是在構造函數中進行。正如其他幾張海報所提到的,您的構造函數應該簡單地將您的類初始化爲已知狀態。例如,

Person() { 
    name = NULL; 
    age = 0; 
} 

然後在您的初始化函數中,您可以分配值。查看你原來的init()函數,可能應該提到,簡單地將一個指針值(char *)分配給另一個指針(char *)只會複製指針的值,而不是它所表示的數據。因此,爲了分配名稱值,您需要計算所需緩衝區的大小,分配緩衝區並自行復制數據。一個基本的init()函數可能看起來像

init(const char *n, int a) { 
    // Calculate the required name length plus a NULL-terminator 
    size_t nameLen = strlen(n) + 1; 

    // Free any previous value. 
    if (name != NULL) { 
     delete[] name; 
    } 

    // Allocate the name buffer and copy the name into it. 
    name = new char[nameLen]; 
    memcpy(name, n, nameLen); 

    // Store the age. 
    age = a; 
} 

最後,在你的析構函數,你釋放你的類分配的資源,在這種情況下,名稱緩衝區。

~Person() { 
    if (name != NULL) { 
     delete[] name; 
    } 
} 

如果你有一本書或與您的類相關聯的東西,你可能要審查指針的信息。他們可能有點棘手,但重要的是學習。我懷疑這就是爲什麼使用char *指定字符串而不是STL字符串類的原因。

對於將信息放置在頭文件和源文件中的問題,創建包含類聲明和成員函數原型的頭文件通常被認爲是很好的做法,然後在單獨的源文件中提供方法的實現。對於一些簡單的函數,您可以直接在頭文件中提供實現。

在一個單獨的源文件提供類成員定義時,關鍵是要提供類名正常範圍的函數(即,人::)。所以,你的頭文件可能包含一個類定義像

// Header file (e.g., person.h) 

class Person { 
private: 
    char *name; 
    int age; 

public: 
    Person() { name = NULL; age = 0 }; 
    ~Person() { if (name != NULL) delete[] name; } 

    void init(const char *n, int a); 

    // Other method declarations and/or definitions 
}; 

然後在源文件中使用您的個人類只需要包括你的類定義的頭文件

// Source file (e.g., person.cpp) 

void Person::init(const char *n, int a) { 
    // ... 
} 

// Rest of method definitions 

源文件。

0

除了以前發佈的答案,我有兩點建議分給你:

  • 不使用「朋友」。這裏有些人可能不同意我的看法,但'朋友'應該不再是C++的一部分,因爲它違背了OOP的含義。
  • 命名您的方法:避免命名您的方法,如'take_name'或'take_age'。通常情況下,因爲這些是getter,所以可以考慮將它們命名爲'getName'和'getAge'。你最終會以這種方式獲得開發者的更多尊重。
+0

雖然'朋友'當然可以是一種代碼味道,並且通常有更好的方法,但也有一些地方非常需要,包括STL內部。它可能不是純粹的OOP,但C++也不是。然而,對於名字達成一致。 – 2010-05-25 15:33:06