C++ 类中字符串指针的编译错误

C++ 类中字符串指针的编译错误,c++,string,pointers,constructor,destructor,C++,String,Pointers,Constructor,Destructor,我对类和构造函数是新手。此程序要求用户输入两个圆的名称。我定义了一个默认构造函数来设置radius和name的参数,并定义了另一个构造函数来接受它们作为参数。我相信setName有问题,它告诉我构造函数已经定义好了。感谢您的帮助 #include <iostream> #include <cstring> #include <string> using namespace std; class Circle { private: double pi

我对类和构造函数是新手。此程序要求用户输入两个圆的名称。我定义了一个默认构造函数来设置radius和name的参数,并定义了另一个构造函数来接受它们作为参数。我相信setName有问题,它告诉我构造函数已经定义好了。感谢您的帮助

#include <iostream>
#include <cstring>
#include <string>
using namespace std;

class Circle
{
private:
    double pi = 3.14;
    double radius;
    string *name;

public:

    Circle();

    Circle(double, string);

    Circle::Circle()
    {
        radius = 0.0;
        *name = nullptr;

    }

    Circle::Circle(double r, string n)
    {
        radius = r;
        *name = n;
    }

    ~Circle()
    {
        delete[] name;
    }

    void setRadius(double r)
    {
        if (r >= 0)
            radius = r;
        else
        {
            cout << "Invalid radius\n";
            exit(EXIT_FAILURE);
        }
    }

    double getRadius()
    {
        return radius;
    }

    double getArea()
    {
        return pi* radius * radius;
    }

    double getCircumference()
    {
        return 2 * pi * radius;
    }

    void setName(string n)
    {

        *name = n;
    }

    string getName()
    {
        return *name;
    }

};


int main()
{
    Circle circle1;
    Circle circle2;
    double circRad1;
    double circRad2;
    string name1;
    string name2;

    cout << "Enter the name for circle 1: ";
    getline(cin, name1);

    cout << "Enter the name for circle 2: ";
    getline(cin, name2);

    cout << "Enter the radius for cirle 1: ";
    cin >> circRad1;

    cout << "Enter the radius for cirle 2: ";
    cin >> circRad2;

    circle1.setRadius(circRad1);
    circle2.setRadius(circRad2);
    circle1.setName(name1);
    circle2.setName(name2);


    cout << "Circle 1 name: " << circle1.getName() << "\n";
    cout << "Circle 1 radius: " << circle1.getRadius() << "\n";
    cout << "Circle 1 area: " << circle1.getArea() << "\n";
    cout << "Circle 1 circumfrence: " << circle1.getCircumference() << "\n";
    cout << "\n";

    cout << "Circle 2 name: " << circle2.getName() << "\n";
    cout << "Circle 2 radius: " << circle2.getRadius() << "\n";
    cout << "Circle 2 area: " << circle2.getArea() << "\n";
    cout << "Circle 2 circumfrence: " << circle2.getCircumference() << "\n";

    return 0;
}
我看到的问题:

建设者

你有:

Circle();

Circle(double, string);

Circle::Circle()
{
    radius = 0.0;
    *name = nullptr;    
}

Circle::Circle(double r, string n)
{
    radius = r;
    *name = n;
}
这是不正确的,因为前两行声明构造函数,而您使用错误的语法再次声明和定义它们

拆下前两行

名称的使用

不清楚为什么要使用字符串*作为名称。使其成为对象,而不是指针

string name;
然后,将构造函数更改为:

// Use the default constructor to initialize name
Circle() : radius(0.0) {}

Circle(double r, string n) : radius(r), name(n) {}
您可以完全删除析构函数。如果您坚持要使用,请将其更改为不再需要删除名称:

~Circle() {}
将setName更改为:

将getName更改为:


PS您尝试的代码向我表明,您将从一本好书中学习该语言的基础知识中获益。有关想法,请参阅。

我只想补充前面的答案,因为我没有足够的要点来评论

在他的构造函数中,他使用的是所谓的初始值设定项列表:

Circle() : foo(0), bar(0) {}
i、 e

与初始值设定项列表相比:

Circle() : foo(0), bar(0) {}
当您只是初始化变量时,首选的做法几乎总是列表格式。这是因为它在实例化对象之前提供了构造对象的参数。这将允许您构造对象,这些对象的身份直到运行时才知道,即变量类型对象,或者可以接受多个类型的对象,尽管这些不是C++原生的,也不是const值。 我有点怀疑你的指导老师让你在第一门编程课程中创建动态对象,但既然是这样

不能简单地传入字符串对象并将指针指定给它的原因是指针严格地说是指向已存在对象的地址。仅当字符串通过引用传入时,此操作才有效,然后语法可能为:

void foo(std::string& str) {

    name = &str;
}
当您在不使用符号的情况下按值传入时,对象的副本将通过参数传入。这个副本在内存中还没有自己的主目录,而且它肯定不是您在参数中传递的主目录。因此,当您试图将它的地址指定给指针时,编译器想要抱怨,因为您试图保存的地址将在下一个}命中时,这个作用域结束后立即消失

但是,可以使用复制的值创建永久对象。这是当您在堆上分配动态内存时,它通常在堆栈上。这看起来像:

void foo(std::string str) {

    name = new std::string(str);
}
这将允许您的名称指针指向堆上新创建的对象。这就是为什么解构器中需要delete[]表达式,因为编译器无法为您管理动态内存,所以您必须确保在程序结束之前释放它

请注意,之所以需要[],是因为字符串实际上是一个字符数组。动态分配数组时,[]符号将确保在读取sentinel值之前释放内存。哨兵字符在ASCII图表上几乎总是指NULL或0

如果释放的是一个int,那么语法将是:

delete x;
最后一个音符。在私有部分中,有一个名为pi的变量,默认初始化为3.14。这大概是因为这是一个经常被提及的值,在所有圈子中都很常见。当您拥有在该类的每个实例中都相同的公共变量时,您将需要使用所谓的静态变量。这是一个分配一次的变量,与该变量关联的每个人都可以访问该变量。另外,因为您不希望pi改变,所以它应该是const。可能是这样的:

private:
    static const double PI = 3.14;

这将创建一个名为PI的对象,并且在您创建的每个圆中都将使用完全相同的PI对象。假设您可以创建多个对象,这将大大减少该对象的内存使用。同样值得注意的是,通常常量变量是大写的,非常量变量不是。

我同意@RSahu提出的所有观点,但将尝试回答您的具体问题

免责声明:在本作业中使用指针是不必要的,也是危险的。在这种情况下要求使用指针更不寻常,因为指针对于初学者来说是一个众所周知的难以掌握的概念

定义构造函数

每个构造函数定义两次

Circle();

Circle::Circle()
{
    // ...
}
然后

Circle(double, string);

Circle::Circle(double r, string n)
{
    // ...
}
您只需要定义它们一次。如果同时声明和定义它们,那么以下内容就足够了:

Circle()
{
    // ...
}
如果要单独声明和定义它们,则可以执行以下操作:

class Circle
{
public:

    // Declare the constructor
    Circle();

};

// Then later in some source, define it
Circle::Circle()
{
    // ...
}
实施 建设者

两个构造函数都存在严重错误,忽略了被迫使用string*的事实

首先,

Circle()
{
    radius = 0.0;
    *name = nullptr;
}
执行*name=nullptr时,将取消对名称指针的引用并将其分配给nullptr

这是不好的,原因有多种:

名称尚未设置。您正在解除对垃圾指针的引用并将其设置为nullptr。这是一次撞车

即使名称已经初始化,您也要将它指向的字符串对象设置为nullptr,这是另一个崩溃

正确的初始化方法如下:

Circle()
    : radius{ 0.0 },
      name{ nullptr }
{

}
现在让我们看看另一个构造函数

Circle(double r, string n)
{
    radius = r;
    *name = n;
}
同样,半径设置基本正确,但我们在名称方面存在重大问题

名称再次未初始化。因此,我们正在设置一个name指向n的不存在字符串。 现在,我们实际上幸免了一点好运。如果你在表演

name = &n;
那就不好了,因为n是一个临时对象。一旦我们离开构造函数,我们的名字就会指向垃圾,下次尝试访问它时就会崩溃

但是我们如何修复这个构造函数呢?我想这样做:

Circle(double const r, string n)
    : radius{ r },
      name{ new string{n} }
{

}
在名称{newstring{n}}中,我们将名称设置为一个新字符串对象,该对象由n中的值初始化

希望您开始理解为什么在我的免责声明中我不同意使用字符串*

固定集合名

因此,您的setName实现几乎可以

如果我们用第二个构造器创建一个圆形的对象,就可以了。name指向的字符串将简单地设置为n的值

但是,如果我们使用的是通过第一个构造函数创建的圆呢?然后,我们将取消对nullptr的引用,并尝试将其设置为n的值。撞车

实际上,我会在您的第一个构造函数中将此问题更改为:

Circle()
    : radius{ 0.0 },
      name{ new string }
{

}
所以现在我们知道name总是指向一个有效的string对象

最后是析构函数

在析构函数中,使用的删除[]不正确

删除对象的动态数组时使用delete[]。字符串是单个对象,因此应该使用delete

我个人也认为,将任何已删除的指针设置为nullptr是一个好习惯,这样任何常见的nullptr检查都会工作,并且不会因为垃圾而失败

~Circle()
{
    delete name;
    name = nullptr;
}

这是一个家庭作业,量规希望它是一个字符串指针。非常感谢你的建议和这本书的链接。我非常感激。我是一名大学一年级新生,在第一年学习其他课程很难。这会有很大帮助。@NacDan,很抱歉听到你不得不使用字符串*。祝你在课堂上的剩余时间好运。。。您是否暗示delete[]new std::string;不是UB吗?谢谢大家的帮助!!!很抱歉反应太晚,但我很感谢你为帮助像我这样的新手所做的一切。抽出时间来帮助我让艰难的日子变得容易一些。再次感谢你们!
Circle()
    : radius{ 0.0 },
      name{ new string }
{

}
~Circle()
{
    delete name;
    name = nullptr;
}