简体   繁体   English

当在类对象内传递函数时,std :: string会丢失值

[英]std::string loses value when passed in function inside a class object

I am really confused how compiler allocates STL objects. 我真的很困惑编译器如何分配STL对象。 Consider the following code: 考虑以下代码:

#include <string>

using namespace std ;

class s { 
    public:
    string k ; 

    s(string k) : k(k) {}
} ;

void x ( s obj ) {
    string k = (obj.k) ;
    k += "haha" ;

}



int main () {
    std::string mystr ("laughter is..") ;
    s mys(mystr) ;
    x(mys) ;

    printf ("%s", mystr.c_str() ) ;
}

The output of this program is laughter is.. and I expect the output to be: laughter is haha 这个程序的输出laughter is..我希望输出laughter is.. laughter is haha

Why doesn't mystr string get haha . 为什么mystr字符串不能得到haha I need to store it in a class as a part of my code. 我需要将其存储在类中作为代码的一部分。

If I had passes mystr by value to function x, the string mystr would have got haha into it. 如果我按值将mystr传递给函数x,则字符串mystr可能已经加入了haha

a) How and when do STL objects get allocated? a)如何以及何时分配STL对象? I supposed mystr is on a stack and must be accessible to all functions called from main() . 我认为mystr在堆栈上,并且必须对所有从main()调用的函数都可以访问。

b) What if I need to store STL objects in a old fashioned Linked list which needs "void*". b)如果我需要将STL对象存储在需要“ void *”的老式链接列表中,该怎么办? Cant I just do: 我不能这样做:

std::string mystr ("mystring.." );

MyList.Add((void*)&mystr) ;

fun(MyList) ;

Can the function fun, now use and modify mystr by accessing MyList ? 现在可以通过访问MyList来有趣,使用和修改mystr吗?

c) As an alternative to (b) , can I use pass by reference. c)作为(b)的替代方案,我可以使用引用传递。 The issue is can I declare a class to keep a reference of mystr? 问题是我可以声明一个类来保留对mystr的引用吗? I mean the constructor of MyList can be like this: 我的意思是MyList的构造函数可以是这样的:

class MyList { 
    string& mStr ;
    ...
};


MyList::MyList ( string& mystr ) {
      mStr = mystr ;

}

Is that constructor valid ? 该构造函数有效吗? Is that class valid? 该课程有效吗?

Your class is just complicating the situation for you. 您的课程只会使您的情况复杂化。 You have exactly the same problem here: 您在这里有完全相同的问题:

void x ( string str ) {
    str += "haha" ;
}

int main () {
    std::string mystr ("laughter is..") ;
    x(mystr) ;

    printf ("%s", mystr.c_str() ) ;
}

I've gotten rid of the class. 我已经下课了。 Instead of putting mystr into an s object and passing the s object to x , I just pass mystr directly. mystr直接将mystr传递给s对象并将s对象传递给x ,而是将mystr传递给x x then attempts to add "haha" to the string. x然后尝试在字符串中添加"haha"

The problem is that x takes its argument by value. 问题是x按值接受参数。 If you pass an object by value, you are going to get a copy of it. 如果按值传递对象,则将获得该对象的副本。 That is, the str object is a different object to mystr . 也就是说, str对象是与mystr不同的对象。 It's a copy of it, but it's a different object. 它是它的副本,但它是一个不同的对象。 If you modify str , you're not going to affect mystr at all. 如果修改str ,则完全不会影响mystr

If you wanted x to be able to modify its argument, you'd need to make it take a reference: 如果希望x能够修改其参数,则需要使其带有一个引用:

void x ( string& str ) {
    str += "haha" ;
}

However, I understand why you introduced the class. 但是,我了解您为什么介绍该课程。 You're thinking "Well if I give the string to another object and then pass that object along, the string should be the same both outside and inside the function." 您在想:“如果我将字符串提供给另一个对象,然后再传递该对象,则该字符串在函数的内部和外部都应该相同。” That's not the case because your class is storing a copy of the string. 并非如此,因为您的类正在存储字符串的副本。 That is, your class has a member string k; 也就是说,您的类具有成员string k; which will be part of any object of that class type. 这将是该类类型任何对象的一部分 The string k isn't the same object as mystr . 字符串kmystr是不同的对象。

If you want to modify objects between functions, then you need some form of reference semantics. 如果要在函数之间修改对象,则需要某种形式的引用语义。 That means using pointers or references. 这意味着使用指针或引用。

As for your questions: 至于您的问题:

  1. Yes, the string object mystr is on the stack. 是的, string对象mystr在堆栈中。 That has nothing to do with it coming from the standard library though. 但这与标准库无关。 If you write a declaration inside a function, that object is going to be on the stack, whether it's int x; 如果在函数内编写声明,则该对象将位于堆栈中,无论它是int x;还是int x; , string s; string s; , SomeClass c; SomeClass c; , or whatever. , 管他呢。

    The internal storage of data inside mystr is, on the other hand, dynamically allocated. 另一方面, mystr内部的数据内部存储是动态分配的。 It has to be because the size of a std::string can vary, but objects in C++ always have fixed size. 一定是因为std::string的大小可以变化,但是C ++中的对象始终具有固定的大小。 Some dynamic allocation is necessary. 必须进行一些动态分配。 However, you shouldn't need to care about this. 但是,您不需要关心这一点。 This allocation is encapsulated by the class. 该分配由类封装。 You can just treat mystr as a string. 您可以将mystr视为字符串。

  2. Please don't use a linked list that stores void* s. 请不要使用存储void*的链表。 Use std::list instead. 使用std::list代替。 If you want a linked list of strings, you want std::list<std::string> . 如果要一个字符串链接列表,则需要std::list<std::string> But yes, if you have an object that stores pointers to some other objects and you pass that object around by value, the pointers in the copies will still be pointing at the same locations, so you can still modify the objects that they point to. 但是可以,如果您有一个存储指向其他对象的指针的对象,并按值传递该对象,则副本中的指针仍将指向相同的位置,因此您仍然可以修改它们所指向的对象。

  3. If you have a std::list<std::string> and you want to pass it to a function so that the function can modify the contents of the container, then you need to pass it by reference. 如果您有std::list<std::string>并希望将其传递给函数,以便该函数可以修改容器的内容,则需要通过引用传递它。 If you also need the elements of the list to be references to the objects you created outside the list, you need to use a std::list<std::reference_wrapper> instead. 如果还需要列表的元素引用在列表外部创建的对象,则需要使用std::list<std::reference_wrapper>

    As far as initialising a reference member is concerned, you need to use a member initialisation list: 就初始化参考成员而言,您需要使用成员初始化列表:

     MyList::MyList(string& mystr) : mStr(mystr) { } 

The string k that you manipulate in your function x is a copy of the string k in your object obj . 该字符串k您在功能操作x是字符串的一个副本k在你的对象obj And obj itself is already a copy of what you pass and the string you pass and store in obj is also already a copy. 而且obj本身已经是您传递的内容的副本,并且传递并存储在obj中的字符串也已经是副本。 So it's very, very far from the original mystr that you expect to being altered. 因此,它与您希望更改的原始mystr非常非常不同。

To your other questions: a) Yes, objects in this way are stack allocated. 对于您的其他问题:a)是,以这种方式分配对象。 Not just stl, any objects. 不只是stl,还有任何对象。 Otherwise you need to use new . 否则,您需要使用new b) No you cannot pass it like this, since it's stack allocated the memory will become invalid. b)不,您不能像这样传递它,因为它已分配了堆栈,因此内存将变为无效。 You need to heap allocate it using new. 您需要使用new对其进行堆分配。 c) Yes you can pass by reference, but again, it's important where you allocate things. c)是的,您可以引用作为参考,但同样,在您分配内容的地方也很重要。

As others point out, those are some very basic questions and you need t read about heap vs stack allocation and pass by reference and pass by value first and then have a look at some basic STL classes and containers. 正如其他人指出的那样,这是一些非常基本的问题,您无需阅读有关堆与堆栈分配的信息,无需先通过引用传递并通过值传递,然后查看一些基本的STL类和容器。

Strictly speaking, your question has nothing to do with the STL, even if you accept "STL" as a synonym for the correct "containers, iterators and algorithms of the C++ standard library". 严格来说,即使您接受“ STL”作为正确的“ C ++标准库的容器,迭代器和算法”的同义词,您的问题也与STL无关。 std::string was at one point of is history made to appear like a container (a container of characters, that is), but it is generally used in quite a different fashion than "real" container classes like std::vector or std::set . std::string只是在历史上使它看起来像一个容器(就是一个字符容器),但它通常以与“ std::vectorstd::set ”等“真实”容器类完全不同的方式使用std::set

Anyway, 无论如何,

Why doesnt mystr string get "haha" 为什么mystr字符串没有得到“哈哈”

Because you don't use references . 因为您不使用引用 x modifies a copy of the argument; x修改参数的副本 likewise, string k = (obj.k) creates a copy of the string. 同样, string k = (obj.k)创建该字符串的副本 Here is the code with references: 这是带有参考的代码:

void x ( s &obj ) {
    string &k = (obj.k) ;
    k += "haha" ;
}

a) How and when do STL objects get allocated? a)如何以及何时分配STL对象?

The container object itself is allocated as you define it. 容器对象本身是在定义时分配的。 How it allocates memory internally is defined by its allocator template parameter, by default std::allocator . 它如何在内部分配内存由其分配器模板参数定义,默认情况下为std::allocator You don't really want to know the internals of std::allocator - it almost always does the right thing. 您实际上并不想要了解std::allocator的内部知识-它几乎总是做正确的事情。 And I don't think your question is about internal allocations, anyway. 而且我认为您的问题不是关于内部分配的。

I supposed mystr is on a stack and must be accessible to all functions called from main() 我认为mystr在堆栈上,并且必须对所有从main()调用的函数都可访问

Yes. 是。

b) What if I need to store STL objects in a old fashioned Linked list which needs "void*". b)如果我需要将STL对象存储在需要“ void *”的老式链接列表中,该怎么办?

Use std::list<void*> . 使用std::list<void*>

But you don't have to do this. 但是您不必这样做。 Use std::list<std::string> and you likely won't need pointers in your code at all. 使用std::list<std::string> ,您可能根本不需要代码中的指针。

As for your further code examples: 至于您的其他代码示例:

std::string mystr ("mystring.." );
MyList.Add((void*)&mystr) ;
fun(MyList) ;

Can the function fun, now use and modify mystr by accessing MyList ? 现在可以通过访问MyList来有趣,使用和修改mystr吗?

Yes. 是。 However, the code has two problems. 但是,代码有两个问题。 The smaller one is (void*)&mystr . 较小的是(void*)&mystr Generally, you should avoid C-style casts but use one of static_cast , reinterpret_cast , const_cast or dynamic_cast , depending on which conversion you need. 通常,应避免使用C样式强制转换,而是根据需要的转换使用static_castreinterpret_castconst_castdynamic_cast And in this piece of code, you don't need a cast at all, anyway. 而且在这段代码中,您根本不需要强制转换。

The bigger problem is adding the address of a local variable to something which looks like it expects dynamically allocated objects. 更大的问题是将局部变量的地址添加到看起来像期望动态分配对象的对象中。 If you return MyList from a function, mystr will be destroyed and the copied list will contain a pointer to a dead object, eventually leading to undefined results. 如果返回MyList从功能, mystr将被销毁,复制列表将包含一个指向死对象,最终导致不确定的结果。

In order to solve this, you have to learn more about new , delete and, possibly, smart pointers. 为了解决这个问题,您必须了解有关newdelete以及可能的智能指针的更多信息。 This is beyond the scope of a simple answer, and the outcome would probably still be worse than std::list<std::string> . 这超出了简单答案的范围,其结果可能仍会比std::list<std::string>

The issue is can I declare a class to keep a reference of mystr? 问题是我可以声明一个类来保留对mystr的引用吗?

Yes, but you should generally avoid it, because it easily leads to dangling references, ie references to dead objects, for the reasons explained above. 是的,但是您通常应该避免使用它,因为出于上述原因,它很容易导致悬空引用(即对死对象的引用)。

class MyList { 
    string& mStr ;
    ...
};


MyList::MyList ( string& mystr ) {
      mStr = mystr ;

}

Is that constructor valid ? 该构造函数有效吗?

No, it won't compile. 不,它不会编译。 You'd need to use an initialisation list: 您需要使用初始化列表:

MyList::MyList ( string& mystr ) : myStr(mystr) {}

I can only repeat my recommendation from above. 我只能从上面重复我的建议。 Use std::list<std::string> . 使用std::list<std::string>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 当我将该类放入另一个类的地图中时,类字符串变量将失去其值 - Class string variable loses its value when I put that class inside a map in another class 为什么传递给模板函数的std :: string对象不喜欢std :: string重载? - Why does a std::string object passed to a template function not prefer std::string overload? 在作为字符串参数传递的 function 中调用 class 方法 - Call a class method inside a function which was passed as a string parameter 没有对象就无法调用成员函数std :: string class :: function() - Cannot call member function std::string class::function() without object std::function 内部模板 class 给出 object 创建错误 - std::function inside template class giving object creation error 为什么 class 需要移动操作来绑定到 std::function ,该签名具有 class 按值传递的签名? - Why would a class need move operations to bind to a std::function that has a signature in which the class is passed by value? 显式传递模板参数时,函数模板参数会丢失常量吗? - function template parameter loses const when template argument is explicity passed? 传递给方法时std :: string不同 - std::string is different when passed to method 在 C++ 中将字符串文字传递给接受 const std::string &amp; 的函数时会发生什么? - What happens when a string literal is passed to a function accepting a const std::string & in C++? 字符串未传递到类函数中 - String not getting passed into class function
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM