繁体   English   中英

学习C ++向量...我这样做正确吗? 我对vector :: push_back()的反复调用可以简化吗?

[英]Learning C++ vectors… Am I doing this correctly? Can my repeated calls to vector::push_back() be simplified?

首先,我要说这个程序绝不是有用的。 这只是我写的东西来测试和学习如何使用向量。

在此示例中,我正在采取一些我正在从事的现实生活项目中需要做的动作。 我不得不从Google上大约20个不同的例子中找出如何做很多事情。

这是我第一次对向量做任何事情, 我想确保自己所做的一切都是安全的 我还想知道是否可以简化对vector::push_back()重复调用? 请注意,该代码确实可以按照我的预期工作。 我只是想看看我是否忽略了任何东西,误解了什么,拨打不安全的电话等。

最后一件事... 我也想学习如何使用智能指针( auto_ptr )。 但是到目前为止,我还没有看到这个例子会受益吗?

#include <iostream>
#include <fstream>
#include <vector>

using namespace std;
typedef vector<unsigned char> message;


void append_file( message& buffer, int header_size )
{

    ifstream is("vector",ifstream::binary);
    is.seekg(0,ios::end);
    int length = is.tellg();
    is.seekg(0, ios::beg);

    buffer.reserve( header_size + length );
    buffer.resize( header_size +length );

    is.read( (char*)&buffer[header_size], length );
    is.close();

    buffer.at(4) = (buffer.size() & 0xFF00) >> 8;
    buffer.at(5) = buffer.size() & 0xFF;

}

int main( int argc, char *argv[] )
{

    while( true ) //this is just for me to watch memory usage
    {
        message msg;

        msg.push_back(0x00);
        msg.push_back(0x00);
        msg.push_back(0x01);
        msg.push_back(0x82);
        msg.push_back(0x00);
        msg.push_back(0x06);

        msg.push_back(0x01);
        msg.push_back(0x01);
        msg.push_back(0x20);
        msg.push_back(0x01);
        msg.push_back(0x25);
        msg.push_back(0x04);

        int header = msg.size();

        append_file( msg, header );

        ofstream os("test.dt",ofstream::binary);
        os.write( (char*)&msg[0], msg.size() );
        os.close();

        cout << "Wrote " << msg.size() << " bytes" << endl;

        cout << "Press any key to repeat...";
        cin.get();

    } //it is my understanding from my reading on vectors that the memory will be de-allocated here. Is this correct? It seems to be? At least on Linux.

    return 0;

}


注意:请不要提及boost,MFC或ATL。

您可以使用以下方法简化msg初始化:

#include <algorithm>

message msg(10);
std::copy_n("\x00\x00\x01\x82\x00\x06\x01\x01\x20\x01\x25\x04", msg, 10);

我还想知道是否可以简化对vector :: push_back()的重复调用?

不多。 在C ++ 11中,您可以使用元素列表( message msg = {0x00, 0x00, 0x01.....}初始化向量,但这需要编译器支持此功能,并且您的编译器很有可能尚不支持。

最后一件事...我也想学习如何使用智能指针(auto_ptr)。 但是到目前为止,我还没有看到这个例子会受益吗?

你是对的。 神奇的词是RAII:资源获取是初始化。 这意味着资源应归C ++对象所有,这些对象负责获取和释放资源。 智能指针是执行此操作的一种方法(您有一个指向某些已分配堆的内存的指针,并将其包装在一个智能指针中,该指针负责在时间到时释放内存),但使用std::vector (或其他标准库容器也实现RAII语义,它们拥有放置在其中的对象,并在需要释放它们时负责释放它们。

您自己的自建对象也可以这样做。 在这方面,智能指针没有什么特别的,它们只是RAII化原始指针的便捷捷径。 但是,还有许多其他方法可以使您的资源使用RAII。

(我的烦恼之一是,许多人似乎认为RAI​​I只是关于智能指针(特别是shared_ptr ):如果要避免内存泄漏,则需要将所有内容都放在shared_ptr 。这是不正确的。重要的是,您的对象设计时具有清晰的所有权语义,以便始终清楚哪个对象拥有资源(资源可能是内存分配,文件句柄,窗口,网络套接字或任何其他需要(被获取和释放),以便所有者负责释放拥有的资源。智能指针只是行为类似的预制类。

关于auto_ptr ,请远离它。 这不是很有用。 它不能安全地存储在std容器中,并且具有一些古怪的行为,这就是为什么它在C ++ 11中已弃用。

而是使用“新一代”智能指针: shared_ptr (如果您要共享所有权), scoped_ptr (如果您希望将专有的,不可转让的所有权绑定到单个作用域)或unique_ptr ,其作用类似于auto_ptr但是作品

前两个位于Boost(无论如何都应使用)和TR1(可能随编译器一起提供)中,而所有三个都在C ++ 11标准库中。

另一种简化方法:

message m;
std::back_insert_iterator<message> i(m);

i = 0x00;
i = 0x00;
i = 0x01;
i = 0x82;
i = 0x00;
i = 0x06;

i = 0x01;
i = 0x01;
i = 0x20;
i = 0x01;
i = 0x25;
i = 0x04;

这是初始化消息的更好方法:

static const char header[] = { '\x00', '\x00', '\x01', '\x82', '\x00',
                               '\x06', '\x01', '\x01', '\x20', '\x01',
                               '\x25', '\x04' };
vector<char> msg(header, header + (sizeof(header) / sizeof(header[0])));

当然,在C ++ 11中,您可以这样做:

vector<char> msg { '\x00', '\x00', '\x01', '\x82', '\x00', '\x06',
                   '\x01', '\x01', '\x20', '\x01', '\x25', '\x04' };

至于其他地方提到的swap技巧...我认为这不是一个好主意。 除非您的缓冲区有时有绝对巨大的消息,然后返回到相对较小的消息,否则您损失的多于swap技巧带来的收益。 您不希望底层的向量一直在分配内存,而与新的空向量交换会强制发生这种情况。

顺便说一句,在重用向量时,构造函数调用可以替换为assign调用,如下所示:

 msg.assign(header, header + (sizeof(header) / sizeof(header[0]));

另外,您的代码也有问题。 您不验证文件大小是否适合16位值。 这意味着,如果您尝试发送的文件对于邮件中的“大小”字段而言太大,那么您将发现“大小”字段已损坏。 这特别令人烦恼,因为无论大小字段中的大小,您都发送了整个文件。 这意味着在某些情况下,文件的内容可以解释为下一条消息的标题。

幸运的是(尽管我很意外),您使用tellgresizeread确实可以确保您不会发生内存不足异常而崩溃,而不是如果文件太大而无法放入缓冲区, -内存缓冲区。 如果文件大小大于2G,则可能会出现负欠载情况。 如果发生这种情况,我不会冒险猜测会发生什么。 length可能不应该是整数。 找出应该为您提供什么类型的tellg ,然后改用它。

是的,它们可以与boost库Assign( boost boost )附加功能一起使用,或者在C ++ 11中,它们也可以变得更聪明。

总体而言,您无需混合使用预留空间和调整大小。 您应该选择最适合您的。 reserve()保证您可以分配许多项目而无需向量重新分配空间。 resize()表示内存已分配,您可以对其进行写入。

还要记住,按照标准,永远不需要向量来返回它分配的内存。 因此,举例来说,如果向量增长到一百万个元素,然后只需要十个元素,那么该向量将拥有系统内存以存储百万个元素。 这可以通过swap()技巧解决。

std::vector<int> foo(100000);

std::vector<int> small(10);

foo.swap(small); // now when small goes out of scope the 1,000,000 mem allocated
                 // vector shall be freed;

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM