C++ 在这种情况下,在c+中是否可以避免手动管理内存+;?

C++ 在这种情况下,在c+中是否可以避免手动管理内存+;?,c++,pointers,memory-management,reference,C++,Pointers,Memory Management,Reference,我有一个存储类,它保存了事物的列表: #include <iostream> #include <list> #include <functional> class Thing { private: int id; int value = 0; static int nextId; public: Thing() { this->id = Thing::nextId++;

我有一个
存储
类,它保存了
事物的列表

#include <iostream>
#include <list>
#include <functional>

class Thing {
    private:
        int id;
        int value = 0;
        static int nextId;
    public:
        Thing() { this->id = Thing::nextId++; };
        int getId() const { return this->id; };
        int getValue() const { return this->value; };
        void add(int n) { this->value += n; };
};
int Thing::nextId = 1;

class Storage {
    private:
        std::list<std::reference_wrapper<Thing>> list;
    public:
        void add(Thing& thing) {
            this->list.push_back(thing);
        }
        Thing& findById(int id) const {
            for (std::list<std::reference_wrapper<Thing>>::const_iterator it = this->list.begin(); it != this->list.end(); ++it) {
                if (it->get().getId() == id) return *it;
            }
            std::cout << "Not found!!\n";
            exit(1);
        }
};
我的
main()
只需调用
run()
。我得到的结果是:

50
10101
Not found!!
尽管我在寻找:

50
10101
50

问题 函数返回时,本地声明的对象t4似乎不再存在,这很有意义。我可以通过使用
new
动态分配内存来防止这种情况,但我不想手动管理内存

如何在不删除
temp()
函数和不必手动管理内存的情况下修复代码?


如果我只是像一些人建议的那样使用
std::list
,那么
t4
temp
的问题肯定将不再存在,但会出现另一个问题:例如,代码将不再打印
10101
。如果我一直复制内容,我将无法更改存储对象的状态。

谁是存储中对象的所有者?

你真正的问题是所有权。目前,您的
存储
实际上并不包含
内容
,而是由
存储
的用户来管理放入其中的对象的生存期。这与std容器的理念大相径庭。所有对象都拥有您放入其中的对象,容器管理它们的生存期(例如,您只需调用一个向量,最后两个元素就会被销毁)

为什么要引用?

您已经找到了一种使容器不拥有实际对象的方法(通过使用
reference\u wrapper
),但是没有理由这样做。对于一个名为
Storage
的类,我希望它能够保存对象,而不仅仅是引用。此外,这为许多令人讨厌的问题打开了大门,包括未定义的行为。例如:

void temp(Storage& storage) {
    storage.findById(2).add(1);
    Thing t4; t4.add(50);
    storage.add(t4);
    std::cout << storage.findById(4).getValue() << "\n";
}
然后

要使用
存储
执行此操作,只需使
findById
返回一个引用即可。作为演示:

struct foo {
    private: 
        int data;
    public:
        int& get_ref() { return data;}
        const int& get_ref() const { return data;}
};

auto x = foo();
x.get_ref = 12;
TL;DR


如何避免人工资源管理?让其他人帮你做,并称之为自动资源管理:P

谁是存储中物品的所有者?

你真正的问题是所有权。目前,您的
存储
实际上并不包含
内容
,而是由
存储
的用户来管理放入其中的对象的生存期。这与std容器的理念大相径庭。所有对象都拥有您放入其中的对象,容器管理它们的生存期(例如,您只需调用一个向量,最后两个元素就会被销毁)

为什么要引用?

您已经找到了一种使容器不拥有实际对象的方法(通过使用
reference\u wrapper
),但是没有理由这样做。对于一个名为
Storage
的类,我希望它能够保存对象,而不仅仅是引用。此外,这为许多令人讨厌的问题打开了大门,包括未定义的行为。例如:

void temp(Storage& storage) {
    storage.findById(2).add(1);
    Thing t4; t4.add(50);
    storage.add(t4);
    std::cout << storage.findById(4).getValue() << "\n";
}
然后

要使用
存储
执行此操作,只需使
findById
返回一个引用即可。作为演示:

struct foo {
    private: 
        int data;
    public:
        int& get_ref() { return data;}
        const int& get_ref() const { return data;}
};

auto x = foo();
x.get_ref = 12;
TL;DR


如何避免人工资源管理?让其他人帮你做这件事,并称之为自动资源管理:P

t4
是一个临时对象,在退出
temp()
时被销毁,而你存储在
存储中的内容将成为一个悬空引用,导致UB

不太清楚您想要实现什么,但是如果您想保持
存储
类不变,那么应该确保存储在其中的所有引用至少与
存储
本身一样长。您已经发现这是STL容器保留其元素的私有副本的原因之一(其他原因可能不那么重要,因为在某些情况下消除了额外的间接性和更好的局部性)


另外,请停止编写这些
this->
并了解构造函数中的初始化列表,好吗?>_
t4
是一个临时对象,它在退出
temp()
时被销毁,您存储在
存储器中的内容将成为一个悬空引用,导致UB

不太清楚您想要实现什么,但是如果您想保持
存储
类不变,那么应该确保存储在其中的所有引用至少与
存储
本身一样长。您已经发现这是STL容器保留其元素的私有副本的原因之一(其他原因可能不那么重要,因为在某些情况下消除了额外的间接性和更好的局部性)


另外,请停止编写这些
this->
并了解构造函数中的初始化列表,好吗?>_ 根据我的估计,就您的代码实际执行的功能而言,您的代码显然过于复杂了。考虑这个代码,它做所有与你的代码相同的事情,但是用更少的样板代码和一种对你的使用来说更安全的方式:

#include<map>
#include<iostream>

int main() {
    std::map<int, int> things;
    int & t1 = things[1];
    int & t2 = things[2];
    int & t3 = things[3];
    t1 = 10;
    t2 = 100;
    t3 = 1000;
    t2++;
    things[4] = 50;
    std::cout << things.at(4) << std::endl;
    t2 += 10000;
    std::cout << things.at(2) << std::endl;
    std::cout << things.at(4) << std::endl;
    things.at(2) -= 75;
    std::cout << things.at(2) << std::endl;
    std::cout << t2 << std::endl;
}

//Output:
50
10101
50
10026
10026
这些代码片段应该让您对语言的工作原理有一个很好的了解,以及您需要了解哪些原则,这些原则可能是我编写的代码中不熟悉的。我的一般建议是找到一个好的C++资源来学习语言的基础知识,并从中学习。一些好的资源

最后一件事:如果使用<代码>东西>代码对代码非常重要,因为您需要在地图中保存更多的数据,请考虑这一点:

#include<map>
#include<iostream>
#include<string>

//Only difference between struct and class is struct sets everything public by default
struct Thing {
    int value;
    double rate;
    std::string name;
    Thing() : Thing(0,0,"") {}
    Thing(int value, double rate, std::string name) : value(value), rate(rate), name(std::move(name)) {}
};

int main() {
    std::map<int, Thing> things;
    Thing & t1 = things[1];
    t1.value = 10;
    t1.rate = 5.7;
    t1.name = "First Object";
    Thing & t2 = things[2];
    t2.value = 15;
    t2.rate = 17.99999;
    t2.name = "Second Object";

    t2.value++;
    std::cout << things.at(2).value << std::endl;
    t1.rate *= things.at(2).rate;
    std::cout << things.at(1).rate << std::endl;

    std::cout << t1.name << "," << things.at(2).name << std::endl;
    things.at(1).rate -= 17;
    std::cout << t1.rate << std::endl;
}
#包括
#包括
#包括
//struct和class之间的唯一区别是默认情况下struct将所有内容都设置为公共
结构物{
int值;
双倍费率;
std::字符串名;
Thing():Thing(0,0
#include<map>
#include<iostream>

int main() {
    std::map<int, int> things;
    int & t1 = things[1];
    int & t2 = things[2];
    int & t3 = things[3];
    t1 = 10;
    t2 = 100;
    t3 = 1000;
    t2++;
    things[4] = 50;
    std::cout << things.at(4) << std::endl;
    t2 += 10000;
    std::cout << things.at(2) << std::endl;
    std::cout << things.at(4) << std::endl;
    things.at(2) -= 75;
    std::cout << things.at(2) << std::endl;
    std::cout << t2 << std::endl;
}

//Output:
50
10101
50
10026
10026
#include<map>
#include<iostream>

int & insert(std::map<int, int> & things, int value) {
    static int id = 1;
    int & ret = things[id++] = value;
    return ret;
}

int main() {
    std::map<int, int> things;
    int & t1 = insert(things, 10);
    int & t2 = insert(things, 100);
    int & t3 = insert(things, 1000);
    t2++;
    insert(things, 50);
    std::cout << things.at(4) << std::endl;
    t2 += 10000;
    std::cout << things.at(2) << std::endl;
    std::cout << things.at(4) << std::endl;
    things.at(2) -= 75;
    std::cout << things.at(2) << std::endl;
    std::cout << t2 << std::endl;
}

//Output:
50
10101
50
10026
10026
#include<map>
#include<iostream>
#include<string>

//Only difference between struct and class is struct sets everything public by default
struct Thing {
    int value;
    double rate;
    std::string name;
    Thing() : Thing(0,0,"") {}
    Thing(int value, double rate, std::string name) : value(value), rate(rate), name(std::move(name)) {}
};

int main() {
    std::map<int, Thing> things;
    Thing & t1 = things[1];
    t1.value = 10;
    t1.rate = 5.7;
    t1.name = "First Object";
    Thing & t2 = things[2];
    t2.value = 15;
    t2.rate = 17.99999;
    t2.name = "Second Object";

    t2.value++;
    std::cout << things.at(2).value << std::endl;
    t1.rate *= things.at(2).rate;
    std::cout << things.at(1).rate << std::endl;

    std::cout << t1.name << "," << things.at(2).name << std::endl;
    things.at(1).rate -= 17;
    std::cout << t1.rate << std::endl;
}
#include <iostream>
#include <list>
#include <memory>

class Thing {
    private:
        int id;
        int value = 0;
        static int nextId;
    public:
        Thing() { this->id = Thing::nextId++; };
        int getId() const { return this->id; };
        int getValue() const { return this->value; };
        void add(int n) { this->value += n; };
};
int Thing::nextId = 1;

class Storage {
    private:
        std::list<std::shared_ptr<Thing>> list;
    public:
        void add(const std::shared_ptr<Thing>& thing) {
            this->list.push_back(thing);
        }
        std::shared_ptr<Thing> findById(int id) const {
            for (std::list<std::shared_ptr<Thing>>::const_iterator it = this->list.begin(); it != this->list.end(); ++it) {
                if (it->get()->getId() == id) return *it;
            }
            std::cout << "Not found!!\n";
            exit(1);
        }
};

void add_another(Storage& storage) {
    storage.findById(2)->add(1);
    std::shared_ptr<Thing> t4 = std::make_shared<Thing> (); t4->add(50);
    storage.add(t4);
    std::cout << storage.findById(4)->getValue() << "\n";
}

int main() {
    std::shared_ptr<Thing> t1 = std::make_shared<Thing> (); t1->add(10);
    std::shared_ptr<Thing> t2 = std::make_shared<Thing> (); t2->add(100);
    std::shared_ptr<Thing> t3 = std::make_shared<Thing> (); t3->add(1000);

    Storage storage;
    storage.add(t3);
    storage.add(t1);
    storage.add(t2);

    add_another(storage);

    t2->add(10000);

    std::cout << storage.findById(2)->getValue() << "\n";
    std::cout << storage.findById(4)->getValue() << "\n";
    return 0;
}
50
10101
50
class Storage {
 private:
   std::list<Thing*> list;
 public:
   void add(Thing& thing) {
     this->list.push_back(&thing);
   }
   Thing* findById(int id) const {
     for (auto thing : this->list) {
       if (thing->getId() == id) return thing;
     }
     std::cout << "Not found!!\n";
     return nullptr;
   }
};