学习c++矢量.我这样做对吗?可以简化对vector::push_back()的重复调用吗?



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

在这个例子中,我使用了几个在我正在从事的现实项目中需要执行的操作。我必须从谷歌上找到的大约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。

另一种简化方式:

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;

您可以简化初始化msg:

#include <algorithm>
message msg(10);
std::copy_n("x00x00x01x82x00x06x01x01x20x01x25x04", msg, 10);

我也想知道我的重复调用vector::push_back()可以简化吗?

不多。在c++ 11中,你可以用元素列表初始化vector (message msg = {0x00, 0x00, 0x01.....}),但这需要编译器支持这个特性,很可能你的编译器还不支持它。

最后一件事……我也想学习如何使用智能指针(auto_ptr)。但到目前为止,我看不出它对这个例子有什么好处?

你是对的。这个神奇的词是RAII:资源获取是初始化。它的意思是资源应该由c++对象拥有,这些对象负责获取和释放资源。智能指针是做到这一点的一种方法(您有一个指向堆分配内存的指针,并将其包装在智能指针中,它负责在适当的时候释放内存),但是std::vector(或其他标准库容器)也实现了RAII语义。它们拥有放置在其中的对象,并且在需要释放它们时负责释放它们。

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

(我最讨厌的是许多人似乎认为智能指针(特别是shared_ptr)就是RAII的全部内容:如果你想避免内存泄漏,你需要把所有放在shared_ptr中。这不是真的。所有重要的是,你的对象被设计成具有明确的所有权语义,以便始终清楚哪个对象拥有资源(资源可能是内存分配,文件句柄,窗口,网络套接字或任何其他需要获取和释放的东西),并且所有者将负责释放所拥有的资源。智能指针就是这样的预制类。

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

相反,使用"新"一代智能指针:shared_ptr(如果你想要共享所有权),scoped_ptr(如果你想要与单个作用域绑定的排他的,不可转让的所有权),或者unique_ptr,用于行为类似auto_ptr工作的东西。

前两个在Boost(在任何情况下都应该使用)和TR1(可能与编译器一起提供)中,并且这三个都在c++ 11标准库中。

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

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技巧将失去比获得更多的消息。你不希望底层向量一直在分配内存,而与一个新的空向量交换恰好会迫使这种情况发生。

顺便说一句,当您重用vector时,可以将构造函数调用替换为对assign的调用,如下所示:

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

还有,你的代码有问题。您没有验证文件大小是否适合16位值。这意味着,如果你试图发送一个太大的文件,在你的消息的大小字段,你将有一个破碎的大小字段。这尤其令人烦恼,因为无论size字段中有什么,都要发送整个文件。这意味着在某些情况下,文件的内容可能被解释为下一条消息的头。

幸运的是(尽管我认为这是偶然的),您使用tellgresizeread确实可以确保在文件太大而无法放入内存缓冲区时,您将从内存不足异常中崩溃,而不是缓冲区溢出。但如果文件大小大于2G,则有可能出现负运行不足。我不会冒险猜测如果发生这种情况会发生什么。length可能不应该是整数。弄清楚tellg应该给你什么类型,然后使用它。

是的,它们可以与boost库分配(boost assign)附加功能一起使用,或者在c++ 11中它们也可以使其更智能。

总的来说,你不需要混合保留和调整大小。你应该选择最适合你的。Reserve()保证您可以在不使用矢量重新分配空间的情况下分配那么多项。Resize()意味着内存已经被分配,你可以写。

还要记住,根据标准,vector永远不需要返回它所分配的内存。举个例子,如果你的向量增长到100万个元素,之后你只需要10个元素,那么这个向量应该拥有存储100万个元素的系统内存。这可以通过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;

最新更新