C ++代码不打印信息,这是怎么回事?

时间:2019-06-02 20:21:09

标签: c++

我的示例代码有问题。

任务是创建一个至少包含一个动态成员的类,以创建一些对象,打印出某些内容并最终销毁它们。我已经编写了代码,它可以编译而没有任何错误或警告,但是它什么都不会打印(我使用的是VS 2017 Community Edition的Microsoft CL-Compiler)。

有人可以给我提示,我在做什么错吗?

dyn.h:

#include <iostream>
#include <string>

#ifndef __CLASS_DYN__
#define __CLASS_DYN__

class dyn{

    private: std::string Name;

    private: int* Age;

    public: dyn(std::string, int);
    public: dyn(const dyn&);

    public: ~dyn();

    public: std::string toString();

};

#endif

dyn.cpp

#include "dyn.h"

dyn::dyn(std::string Name, int Age){
    this->Name = Name;
    *this->Age = Age;
}

dyn::dyn(const dyn& a){
    this->Name = a.Name;
    *this->Age = *a.Age;
}

dyn::~dyn(){
    delete this->Age;
}

std::string dyn::toString(){
    std::string tmp = "Name of Person " + this->Name;
    return (tmp);
}

main.cpp

#include <iostream>
#include "dyn.h"

int main(){

    dyn* Person1 = new dyn{"Mike", 38};
    dyn* Person2 = new dyn{"Thomas", 20};
    dyn* Person3 = Person1;

    std::cout << Person1->toString() << std::endl;
    std::cout << Person2->toString() << std::endl;
    std::cout << Person3->toString() << std::endl;

    delete Person1;
    delete Person2;
    delete Person3;

    return 0;
}

3 个答案:

答案 0 :(得分:3)

在您分配给Age的两行中。在这些情况下,由于Age被声明为int *,因此*this->Age本身并不是this->Age,而是this->Age指向的内存地址值。此外,由于它们都在构造函数中,this->Age尚未拥有有效的地址,因此尝试为其分配值将导致未定义的行为。

正确的做法是通过如下分配内存来确保this->Age在分配之前具有有效地址:

dyn::dyn(std::string Name, int Age){
    this->Name = Name;
    this->Age = new int();
    *this->Age = Age;
}

dyn::dyn(const dyn& a){
    this->Name = a.Name;
    this->Age = new int();
    *this->Age = *a.Age;
}

幸运的是,您已经在析构函数中删除了Age,所以在那里不需要修改。

答案 1 :(得分:1)

Thx。到@HolyBlackCat:

我更改了我的ctor ::

dyn::dyn(std::string Name, int Age){
    this->Name = Name;
    this->Age = new int(Age);
}

dyn::dyn(const dyn& a){
    this->Name = a.Name;
    this->Age = new int{*a.Age};
}

现在它可以按预期工作了:)

答案 2 :(得分:1)

  

但想学习c ++

因此,当您已经对问题本身有一个适当的answer时,我可能会给您一些其他提示...

首先,您已经发现了,可以将参数传递给指针的构造函数本身:

this->Age = new int(Age);
this->Age = new int{*a.Age};

Solely:有两种调用构造函数的方法,一种是带有括号的经典方法,另一种是通过花括号进行新的统一初始化。尽管后者显示出一些良好的意图,但我个人认为它是无效的,并且不在我自己的代码中使用(最喜欢的示例:std::vector<int>{1, 2, 3}调用std::initializer_list constructorstd::vector<int>{7}调用经典构造函数来创建具有七个元素的向量;将其与std::vector<int>(7)std::vector<int>({7})比较,后者显式调用std::initializer_list构造函数)。好吧,您可能想进一步探讨该主题,并决定您自己是要跟随我还是赞成UI的人,但是无论您选择哪种方式,都应该始终使用而不是在两者之间切换...

然后习惯于使用构造函数的初始化程序列表(不要与std::initializer_list混淆!):

dyn::dyn(std::string Name, int Age)
    : Name(Name)
//          ^ function argument
//      ^ member
{
    // ...
}

好吧,甚至 都可以使用相同的标识符(位置明确了哪个是哪个),您仍然可能更喜欢使用不同的标识符。本机类型(int,double,指针,...)和复杂类型并没有太大区别,但是事情有所变化:

dyn::dyn(std::string Name, int Age)
    // default constructor for Name is  s t i l l  called!
{
    this->Name = Name; // and now you do the assignment!
}

默认构造和赋值两者都可能或多或少地花费,在任何情况下,直接初始化(如通过使用前面所示的初始化器列表所发生的)效率更高。此外,引用和非默认可构造类型 only 可以通过这种方式初始化。

然后参数类型:C#根据类型确定是通过值还是通过引用传递参数;在C ++中,您可以按引用或按值(或第三个选项,通过指针)传递任何类型,但是您需要明确!

dyn::dyn(std::string   Name, int Age) // by value (i. e. you make a  c o p y  of!)
dyn::dyn(std::string&  Name, int Age) // by reference
dyn::dyn(std::string*  Name, int Age) // by pointer
dyn::dyn(std::string&& Name, int Age) // for completeness: r-value reference

最后一个是允许移动语义的新概念(即,将内容从一个对象移动到另一个对象)。 tutorial已经很不错了,所以我不再赘述。

但是,通过接受引用,可以避免一个不必要的副本(并且由于您不打算更改该参数,因此应为const):

dyn::dyn(std::string const& Name, int Age);

然后看看rule of three。的确,您获得了由C ++定义的默认赋值运算符,但是它将仅执行以下操作:

dyn::operator=(dyn const& other)
{
    Name = other.Name;
    Age = other.Age;    // now both objects will point to the same address
                        // and you'll get a double deletion error!
}

好的,也定义了移动分配,但不会改变指针的任何内容。借助移动语义,Ro3扩展到了rule of five;尽管前者是(仍然)强制性的,但后者不是强制性的,但是如果您不遵循它,则会错过很大的优化可能性...

处理动态分配的内存的现代C ++方法是使用智能指针,它们有std::unique_ptrstd::shared_ptrstd::weak_ptr(后者很少使用)。

class dyn
{
private:
    std::unique_ptr<int> Age;
public:
    dyn(int Age) : Age(std::make_unique<int>(Age)) { }
};

猜猜是什么:现在默认生成的析构函数,移动和复制构造函数以及赋值 all 已经可以了,您不需要显式实现任何这些...

return不是一个函数,因此您不应在返回值周围使用括号。在C ++中,这甚至可以产生具体效果:

  1. 它防止copy elision返回值。
  2. 在一个相当特定的情况下,您甚至可以创建一个悬空引用:
    decltype(auto) f() { int n; return (n); } // returns a reference to n!

最后,关于命名约定的最后一点。通常,类标识符以大写字母开头,其中变量(成员,全局变量和局部变量)以小写字母开头。函数标识符不明确,有一些偏爱的大写字母(一种主要来自Microsoft的约定),而有些则更喜欢今天的初始小写字母(这是更古老的约定,并且仍然是更常用的约定)。对于功能,您应该选择其中之一,但无论如何,请始终遵循。