关于复制构造函数和指针

时间:2010-07-13 20:35:48

标签: c++

我正在阅读引文中的一个例子,它所谈论的内容并没有发生。具体来说,任何隐式浅拷贝都应该复制指针的地址,而不仅仅是指向的值(因此是相同的内存地址)。但是,每个pos属性都指向两个不同的内存地址(因此我可以更改一个值而不影响另一个)。我做错了什么?

标题

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

class Yak
{
public:
    int hour;
    char * pos;
    const Yak & toz(const Yak & yk);
    Yak();
};

END HEADER

using namespace std;
const Yak & Yak:: toz(const Yak & yk)
{
    return *this;
}

Yak::Yak()
{
    pos = new char[20];

}

int _tmain(int argc, _TCHAR* argv[])
{
    Yak tom;
    tom.pos="Hi";

    Yak blak = tom.toz(tom);


    cout << &blak.pos << endl;
    cout << &tom.pos << endl;


    system("pause");
    return 0;
}

5 个答案:

答案 0 :(得分:11)

您正在打印指针的地址,而不是他们指向的字符串的地址:

cout << &blak.pos << endl;
cout << &tom.pos << endl;

它们是两个不同的指针,因此它们的地址不同。但是,它们指向相同的字符串:

cout << static_cast<void*>(blak.pos) << endl;
cout << static_cast<void*>(tom.pos) << endl;

(请注意,强制转换static_cast<void*>(tom.pos)。正如Aaron在评论中指出的那样,这是必要的,因为当通过char*输出operator<<时,流库将假定指向的字符成为零终止字符串的第一个字符。输出void*,OTOH,将输出地址。)


请注意,您的代码存在更多错误。这里

Yak tom;

您正在创建一个新对象。它的构造函数分配20个字符,并将其地址存储在tom.pos中。在下一行

tom.pos="Hi";

您要将字符串文字的地址分配给tom.pos,从而丢弃您分配的字节的地址,从而有效地泄露该内存。

另请注意,Yak没有析构函数,因此即使您没有丢弃20个字符,当tom超出范围时,tom.pos也会被销毁,因此丢失的那20个字节的地址。

但是,由于您缺少复制构造函数,当您复制Yak对象时,最终会有两个对象的pos元素指向相同的已分配内存。当他们超出范围时,他们都试图删除那个致命的内存。


要缩短此短消息:使用std::string。这更容易。掌握基本知识,使用语言的安全功能,如std::string,标准库的容器,智能指针。一旦您对这些内容有所了解,请解决手动内存管理问题。

但是,请记住,在做C ++大约15年之后,我认为手动资源管理(内存只是一种资源)容易出错,并试图避免它。如果我必须这样做,我将每个资源隐藏在管理它的对象后面 - 有效地回退到自动内存管理。 :)


您正在阅读哪个“入门”? Lippmann的 C ++ Primer ?如果是这样,哪个版本?如果最近一版Lippmann的书能让你在没有首先展示你的工具来解决这个问题以及如何使用它们的情况下让你失去动态记忆,我会感到惊讶。

答案 1 :(得分:2)

除非我遗漏了某些内容,否则您似乎打印出变量pos的地址,而不是指向的地址。 pos 的地址不同,但指针应该相同

答案 2 :(得分:1)

代码没有多大意义。

toz方法使用Yak但不执行任何操作。为什么?事实上,无论如何,toz应该做什么?

tom.pos =“Hi”不会将Hi复制到您在构造函数中分配的新char [20]数组中。相反,它用指向包含“Hi \ 0”的静态const char数组的指针替换pos指针。你需要使用strcpy。

答案 3 :(得分:0)

您的代码中存在内存泄漏。你在构造时分配了20个字符,并且pos指向分配的内存。但是,然后您将pos指向包含“Hi”的内存而不先删除内存。

在任何情况下,pos都是char *类型的成员变量。 Yak的每个实例都将拥有自己独特的pos。但是因为pos是一个指针,它们可能指向同一块内存(在你的例子中就是这种情况)。

您要打印的是pos变量的地址,该变量的类型为char *,而不是它指向的内存的地址,其类型为char。请记住,指针是一个像任何其他变量一样的变量,它有一个地址和一个值(在指针的情况下,该值是一个地址)。

答案 4 :(得分:-1)

如果您希望blak成为tom的浅表副本,请尝试以下操作,而不是使用您的函数toz

Yak *blak = &tom;

// Note the use of -> ; also, &blak->pos will work
cout << &(blak->pos) << endl; 
cout << &tom.pos << endl;

现在,blak仅指向tom ---它没有任何与之相关的内容。

此外,您的代码还有两个问题:

pos = new char [20];

您在这里分配的内存在您使用Yak对象时尚未释放 - 这将导致内存泄漏(除非您打算在其他地方释放它)。如果pos的使用以与其关联的Yak对象结束(例如tom),那么我建议将delete pos;添加到Yak的析构函数中。或者在sbi建议时,使用std::string

tom.pos =“嗨”;

您将其分配给string literal。这意味着pos将卡在Hi上,这实际上是内存中的只读部分。您无法更改pos中存储的字符串,因此我认为这不是您的意图。因此,在20开头分配pos字节似乎毫无意义。

同样,我建议使用string