Cpp一些基本问题

时间:2010-05-24 23:25:52

标签: c++ oop header-files

我的任务如下: 使用char * name和int age创建类Person。使用动态分配内存为变量,析构函数,函数init和友元函数显示实现构造函数。然后将此类转换为header和cpp文件,并在其他程序中实现。好的,这是我的Person类:

#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()函数是一个友好的函数。我应该以某种方式考虑它吗?

我的考试已经无法完成此任务,但我仍然想知道如何实施它。

8 个答案:

答案 0 :(得分:5)

通过从char *切换到std::string来解决您的许多问题。你会很高兴的。

std::string类负责内存分配,解除分配以及复制。

如果这是作业,请说服您的教授使用std::string作为初学者,并将char *保存为指针部分。还要提醒你的教授,C ++语言与C语言不同。这是其中一个领域。

答案 1 :(得分:4)

使用*delete时,您不需要delete[]。只需为它提供指针变量,例如

delete[] name;

此外,您的take_age成员声称返回int*,但您实际上已返回int成员。如果你想这样做,你需要使用&获取成员的地址。正如@Jerry评论的那样,不是你想在这里做什么。

答案 2 :(得分:3)

虽然这个站点上的某些人显然认为它是完全可以接受的,但是很好的做法(参见Can a constructor return a NULL value?),你应该避免在对象的构造函数中执行诸如流操作之类的操作。在外面读取该流,然后使用结果调用该函数。

也就是说,恕我直言,你应该采取的第一步。

答案 3 :(得分:3)

在典型情况下,管理动态分配的内存的指针和块(例如本例中的名称)对于一个类来说是足够的责任。因此,Thomas Matthews是对的:在这种情况下你应该真正使用字符串。如果您要自己处理它,您仍然应该将该责任分解为自己的类,并将该类的对象嵌入到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-correctness。您将参数作为对Person的引用传递。除非它要修改Person对象(在这种情况下,show()似乎是一个糟糕的名称选择),它应该可以引用一个const对象。相同的一般概念适用于take_age() - 因为它只检索一个值,它应该是一个const函数:

int take_age() const { return age; }

我可能已经试图掩盖太多,所以我暂时闭嘴......

答案 4 :(得分: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之后,才能继续设计问题和朋友功能。

答案 5 :(得分: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字符串类指定的问题。

关于将信息放在头文件和源文件中的问题,通常认为创建包含类声明和成员函数原型的头文件然后在单独的源文件中提供方法的实现是一种好习惯。对于一些简单的函数,您可以直接在头文件中提供实现。

在单独的源文件中提供类成员定义时的关键是提供类名以正确定位函数(即Person::)。所以你的头文件可能包含类定义,如

// 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

使用您的person类的源文件只需要包含带有类定义的头文件。

答案 6 :(得分:0)

我认为你的问题在于这一行: 朋友无效(Person&amp; p); 它需要什么。

  

我是否需要在头文件/实现文件中引入构造函数?   构造函数可以位于.h或.cpp文件中。没关系。通常,如果函数很短,可以将它包含在.h文件中。任何更长的时间都应该放在.cpp。

中      

我的show()函数是一个友好的函数。   不确定你的意思。友元函数存在于类定义之外。你的show函数是在类中定义的,所以不需要成为朋友。

答案 7 :(得分:0)

除了之前发布的答案外,我还有两点建议:

  • 不要使用'朋友'。有些人可能不同意我,但“朋友”应该不再是C ++的一部分,因为它违背了OOP所代表的内容。
  • 命名方法:避免命名“take_name”或“take_age”等方法。通常,因为这些是getter,所以考虑将它们命名为'getName'和'getAge'。你最终会以这种方式得到开发人员的更多尊重。