英文:
How can i initialize an array of 2 objects with the "new" statement, and put the pointer of the first element to a map?
问题
请注意,我是一个初学者,对于类不太熟悉。
这是我想要的一个想法(也是我需要使我的代码更干净和灵活的方法)
在我的函数StartupRoutine(std::map<std::string, MyClass*>& mymap)
内部:
MyClass myobj[2];
myobj[0] = new MyClass(/* 构造函数参数 */);
myobj[1] = new MyClass(/* 构造函数参数 */);
/* 做一些操作并调用一些设置器 */
mymap.insert(std::pair<std::string, MyClass*>("something", myobj)); /* myobj 应该等于 &myobj[0] 对吗? */
我从main
中调用它。我希望这些类保持不变,直到我调用delete
。
我希望以这种方式轻松访问类方法,同时具有类似于mymap["something"]
和mymap["something"]+1
的偏移。
我想要在同一个map
中同时使用单个对象和包含2个或4个对象的数组,出于几个原因。
我已经尝试了很多方法并且查了很多小时的谷歌。
我尝试了像上面的示例那样的东西。
然后我尝试将myobj
初始化为单个指针:
MyClass* myobj;
myobj = new MyClass(/* 构造函数参数 */);
myobj+1 = new MyClass(/* 构造函数参数 */);
我相当确定这是非常错误的,因为我试图访问我不应该访问的内存地址。
编辑:感谢大家的帮助,我将在这里回答评论:关于new
语句,你们都是对的,我是个傻瓜,在尝试了几个小时之后忘记了它。这里有一个更详细的解释我在做什么:
我正在制作一个简单的SDL游戏,我有用于纹理的类。我想要映射我的纹理(可能有更好的处理方法,但这是我想出来使我的代码更灵活(并且看起来更好)的最佳解决方案)。因此,大多数纹理在地图上都是单个对象,但在某些情况下,对于动画,我使用无缝纹理,并且我需要相同图像的不同坐标的更多实例(这两者都是类的属性)。我想要将普通纹理处理为单个对象,并将无缝纹理处理为包含2个或4个对象的数组,放在同一个地图中。我知道我可以像map["water1"]
和map["water2"]
一样单独映射它们,但对我来说非常非常丑陋,我正在寻找更好的解决方案。
顺便说一句,我做所有这些都是出于教育目的。
PS:对不起我不在的时候,但在周末我有工作。
第二次编辑:如果我使用malloc而不是new并这样做会怎样:
MyClass* MyObj = (MyClass*)malloc(sizeof(MyClass)*2);
*MyObj = MyClass(/* 构造函数参数 */);
*(MyObj+1) = MyClass(/* 构造函数参数 */);
mymap.insert(std::pair<std::string, MyClass*>("something", MyObj));
不知何故,当我尝试调用类方法时,它崩溃了:
mymap["something"]->MyMethod();
英文:
Please note that i'm a newbie with classes.
Here's an idea of what i want (and need to make my code more clean and versatile)
Inside my function StartupRoutine(std::map<std::string, MyClass*>& mymap)
:
MyClass myobj[2];
myobj[0] = new MyClass(/* constructor parameters */);
myobj[1] = new MyClass(/* constructor parameters */);
/* do some stuff and call some setters */
mymap.insert(std::pair<std::string, MyClass*>("something", myobj)); /* myobj should be equal to &myobj[0] right? */
I'm calling that from the main
. I want the classes to stay until i call delete
.
I want to do that in order to easily access class methods with both objects with an offset like: mymap["something"] and mymap["something"]+1
.
And i'd like to use the same map
for single objects and arrays of 2 or 4 objects, for several reasons,
I have tried many many ways and google for hours.
I tried something like the example above.
Then i tried to initialize myobj
as a single pointer:
MyClass* myobj;
myobj = new MyClass(/* constructor parameters */);
myobj+1 = new MyClass(/* constructor parameters */);
which i'm pretty sure it's really wrong because i'm trying to access a memory address i'm not supposed to.
EDIT: Thanks everyone for the help, i'm going to answer the comments here: about the new
statement you guys are right i'm an idiot and forgot about that after hours of trying. Here a more detailed explanation of what i'm doing:
I'm making a simple game with SDL and i have my class for the textures. I want to map my texture (probaly there are better ways to handle them but that's the best solution i came up with to make my code more versatile (and better looking)). So most of the textures are in the map will be a single object, but in some cases, for the animations, i use a seamless texture and i need more instances of the same image but with different coordinates (which are both attributes of the class). I'd like to handle normal textures as single objects and seamless textures as arrays of 2 or 4 objects in the same map. i know i can simply map them individually like map["water1"]
and map["water2"]
but to me it's very very ugly and i'm trying to find a better solution.
BTW I'm doing all of this for educational purposes.
PS: sorry for my absence but in weekends i do work.
SECOND EDIT: what if i use malloc and instead of new and do this:
MyClass* MyObj = (MyClass*)malloc(sizeof(MyClass)*2);
*MyObj = MyClass(/* constructor parameters */);
*(MyObj+1) = MyClass(/* constructor parameters */);
mymap.insert(std::pair<std::string, MyClass*>("something", MyObj));
for some reason it crashes when i try to call a class method like:
mymap["something"]->MyMethod();
答案1
得分: 1
你的方法存在一个即时问题,即你试图将MyClass*值存储在MyClass数组中。 new
语句返回一个指针,而不是直接对象引用。你可以使用以下其中一种方法:
使用指针数组。
你可以通过添加额外的内存访问层次,基本上以你目前尝试的方式来做。
MyClass* myobj[2];
myobj[0] = new MyClass(...);
myobj[1] = new MyClass(...);
/* ... */
mymap.insert(std::pair<std::string, MyClass**>("something", myobj));
要获取数组中第i个元素的指针,你可以使用 mymap["something"] + i
。
然而,用这种方式做会很混乱,如果你与双指针频繁交互,可能会导致很多麻烦和段错误。我建议使用后一种方法。
使用vector。
包括头文件 <vector>
以使用标准库的向量。向量是动态长度的数据结构,带有方便的功能,如内置的范围检查。你可以 new
一个向量使其在函数作用域结束后仍保留,只需确保在完成后 delete
它(以及删除放入其中的MyClass*值)。
std::vector<MyClass*>* myobj = new std::vector<MyClass>();
myobj->push_back(new MyClass(...));
myobj->push_back(new MyClass(...));
/* ... */
mymap.insert(std::pair<std::string, std::vector<MyClass*>& >("something", myobj));
然后,你可以使用 mymap["something"]->at(i)
访问向量中的第i个元素,它是指向MyClass对象的指针。
由于你说你会使用不同大小的集合,这可能会为你节省很多麻烦。每当你尝试访问向量范围外的元素时,它都会抛出错误。如果你在使用数组和 mymap["something"] + i
时犯同样的错误,它只会获取数组旁边的任何垃圾内存,并且不会知道区别。
除非你有特定的原因不这样做,我强烈建议使用向量而不是数组和内存算术。
英文:
The immediate issue with your approach is that you are trying to store MyClass* values in a MyClass array. The new
statement returns a pointer, not a direct object reference. You could use one of the following:
Use an array of pointers.
You can do it pretty much the way you are currently trying to do it by adding an extra layer of memory accesses.
MyClass* myobj[2];
myobj[0] = new MyClass(...);
myobj[1] = new MyClass(...);
/* ... */
mymap.insert(std::pair<std::string, MyClass**>("something", myobj));
To get a pointer to the ith element in your array, you can use mymap["something"] + i
.
However, doing it this way is messy and can cause a lot of headaches and segfaults if you are interacting with the double-pointers very much. I would recommend the latter method.
Use a vector.
Include the header <vector>
to use the standard library vectors. Vectors are dynamic-length data structures which come with handy features like built-in range checking. You can new
up a vector to make it stick around after the function scope ends, just make sure you delete
it after you're done (and after you delete the MyClass* values that you put in it).
std::vector<MyClass*>* myobj = new std::vector<MyClass>();
myobj->push_back(new MyClass(...));
myobj->push_back(new MyClass(...));
/* ... */
mymap.insert(std::pair<std::string, std::vector<MyClass*>* >("something", myobj));
Then, you access the ith element in the vector, which is a pointer to a MyClass object, with mymap["something"]->at(i)
.
Since you said you'll be using collections of different sizes, this will probably save you a lot of trouble. Any time you try to access an element outside of a vector's range, it will throw an error. If you make the same mistake using an array and mymap["something"] + i
, it will just grab whatever garbage memory is sitting next to that array in memory and not know the difference.
Unless you have some specific reason not to, I would very much recommend using a vector instead of an array and memory arithmetic.
答案2
得分: 0
以下是您要翻译的内容:
MyClass myobj[2];
定义了一个包含两个 MyClass
实例的数组。不是实例的指针,因此无法使用以下方式:
myobj[0] = new MyClass(/* 构造函数参数 */);
new
提供了一个指向动态分配的 MyClass
的指针,而指向 MyClass
的指针不是 Myclass
。您不能将它们分配。此外,这里有两个 MyClass
:被赋值的和由 new
分配的全新实例,必须有人最终使用 delete
将 new
回收,以防止内存泄漏。
当您定义数组时,它会立即构造数组中的每个元素。如果元素没有默认构造函数(没有参数或只有默认参数的构造函数),则只能使用 列表初始化 定义数组。
假设我们有类似这样的简单内容:
class MyClass
{
public:
MyClass(int, int);
};
它需要两个整数。我们可以使用列表提供这些整数:
MyClass myobj[2] = {
{1, 2},
{3, 4}
};
我们可以在列表中使用现有变量:
MyClass myobj[2] = {
{a, b},
{c, d}
};
我们还可以从函数获取它们:
MyClass myobj[2] = {
{get_int(), get_int()},
{get_int(), get_int()}
};
我可能漏掉了一些其他巧妙的技巧,但您可能不需要它们来完成这个任务。
现在您可以这样做:
mymap.insert(std::pair<std::string, MyClass*>("something", myobj));
但是您必须确保 myobj
的生命周期超过了 mymap
。不要做像这样的事情:
void function_of_doom(map<std::string, MyClass*>& mymap)
{
MyClass myobj[2] = {
{a, b},
{c, d}
};
mymap.insert(std::pair<std::string, MyClass*>("something", myobj));
}
在这里,myobj
将在 mymap
之前超出范围。
顺便说一下:
不要尝试这样的事情:
myobj[0] = *new MyClass(/* 构造函数参数 */);
即使编译器允许它。new MyClass(/* 构造函数参数 */)
创建了一个全新的、动态分配的 MyClass
,并提供了一个指向它的指针,以便您可以找到它。*
解引用指针,获取指向的对象,并将该新对象复制到 myobj[0]
中。所有这些都是完全合法的,看起来您得到了想要的结果。但您失去了指向该新对象的指针,没有了指针,您不知道它在哪里,因此很难跟踪,以便在完成后使用 delete
返回内存。如果要动态分配对象,您始终需要保留对对象的引用,以便在完成后释放它。一旦程序变得更大,这比看起来要困难,因此一般的经验法则是极少使用 new
。
好的,所以我们想要避免“毁灭性”函数。这意味着我们需要动态作用域。是的,您可以使用 new
来获得它,但正如我所说,要极少使用它。我们在这里不需要它。
std::map<std::string, std::vector<MyClass>> mymap;
将 string
映射到 MyClass
的 vector
。我们可以以多种不同的方式将内容放入其中,但最简单的方式可能是:
std::vector<MyClass> myobj = {
{a, b},
{c, d}
};
mymap["something"] = myobj;
这里执行的所有动态分配都是由RRAI观察类执行的。您可以从 vector
中添加和删除 MyClass
,也可以从 map
中添加和删除 vector
。
例如:
mymap["something"].emplace_back(5,6);
要添加和删除:
auto found = // find item
mymap["something"].erase(found);
要删除整个“something”:
mymap.erase("something");
只要对象保留在所属容器中,它就会继续存在。删除一个项目后,它将自动销毁并释放。无需处理指针、new
和 delete
,也不会出现内存泄漏的机会。
“毁灭性”函数现在看起来像这样:
void function_of_NOT_doom(std::map<std::string,
std::vector<MyClass>> mymap)
{
std::vector<MyClass> myobj = {
{a, b},
{c, d}
};
mymap["something"] = myobj;
}
英文:
MyClass myobj[2];
defines an array of two MyClass
instances. Not pointers to instances, so using
myobj[0] = new MyClass(/* constructor parameters */);
is not possible. new
provides a pointer to a dynamically allocated MyClass
and a pointer to a MyClass
isn't a Myclass
. You cannot assign them. Plus you have two MyClass
es here: the assignee and the brand new one allocated by new
, and someone would have to give the new
ed back with delete
eventually to prevent a leak.
When you define an array it immediately constructs every element in the array. If the elements to not have a default constructor (a constructor with no parameters or nothing but defaulted parameters), the array can only be defined with a list.
Say we have something simple like
class MyClass
{
public:
MyClass(int ,int);
};
which requires two integers. We can provide those integers with a list
MyClass myobj[2] = {
{1, 2},
{3, 4}
};
We could use existing variables in the list
MyClass myobj[2] = {
{a, b},
{c, d}
};
and we can also get them from a function
MyClass myobj[2] = {
{get_int(), get_int()},
{get_int(), get_int()}
};
I'm probably missing some other sneaky tricks here, but you probably don't need them for this job.
Now you can
mymap.insert(std::pair<std::string, MyClass*>("something", myobj));
but you have to make absolutely certain that myobj
outlives mymap
. Do not do stuff like
void function_of_doom(map<<std::string, MyClass*>> & mymap)
{
MyClass myobj[2] = {
{a, b},
{c, d}
};
mymap.insert(std::pair<std::string, MyClass*>("something", myobj));
}
where myobj
will go out of scope before mymap
.
Side note:
Don't try stuff like
myobj[0] = *new MyClass(/* constructor parameters */);
even if the compiler allows it. new MyClass(/* constructor parameters */)
created a brand new, dynamically allocated MyClass
floating somewhere in storage and provided a pointer to it so you can find it. the *
dereferences the pointer, getting the pointed-at object, and myobj[0] =
copies that new object into myobj[0]
. All of this is perfectly legal and it looks like you get what you want. But you lost the pointer to that new object, and without the pointer you don't know where it is, making it awesomely hard to track down so you can give the memory back with delete
. If you're going to dynamically allocate and object, you always need to keep a reference to the object so you can free it when when you're done. This is harder than it looks once programs get larger, so the general rule of thumb is to use new
extremely rarely.
OK. So we want the function of doom. That means we need need dynamic scope. Yes you can get that with new
, but like I said, use it rarely. We don't need it here.
std::map<std::string, std::vector<MyClass>> mymap;
maps string
s to a vector
of MyClass
es. There are a hole bunch of different ways we could put stuff in, but the easiest is probably
std::vector<MyClass> myobj = {
{a, b},
{c, d}
};
mymap["something"] = myobj;
all of the dynamic allocation performed here is performed for you and protected by RAII-observing classes. You can add and remove MyClass
es from the vector
s and you can add and remove vector
s from the map
.
eg:
mymap["something"].emplace_back(5,6);
to add and to remove
auto found = // find item
mymap["something"].erase(found);
To remove the entire "something"
mymap.erase("something");
So long as an object remains in in the owning container, it will continue to live. Remove an item and it's automatically destroyed and released for you. No messing around with pointers or new
and delete
and no chance of a memory leak.
The function of doom now looks like
void function_of_NOT_doom(std::map<std::string,
std::vector<MyClass>> mymap)
{
std::vector<MyClass> myobj = {
{a, b},
{c, d}
};
mymap["something"] = myobj;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论