gpt4 book ai didi

c++ - 使用带有 std::vector 和 std::sort 的用户定义类型

转载 作者:行者123 更新时间:2023-12-02 10:21:16 25 4
gpt4 key购买 nike

我一直在尝试将用户定义的类型(或类)与 C++ 标准库容器 - vector 一起使用。我希望能够使用内置的 C++ 类型、int、float、string 等的 vector 来做我喜欢做的通常的事情。但是使用我自己定义的类型。我使用 box 编写了一个小示例程序。类尝试并了解发生了什么。

这是该类的代码:

class box {

private:
float *lengthPtr;
std::string *widthPtr;

public:

box(float a, std::string b) {
std::cout<<"Constructor called" << '\n';
lengthPtr = new float;
*lengthPtr = a;

widthPtr = new std::string;
*widthPtr = b;
}
// copy constructor
box(const box &obj){
std::cout<< "User defined copy constructor called" << '\n';
lengthPtr = new float;
*lengthPtr = obj.check_length();

widthPtr = new std::string;
*widthPtr = obj.check_width();

}
// copy assignment operator
box& operator=(const box &that) {
std::cout<< "Copy assignment operator called";

float *localLen = new float;
*localLen = that.check_length();
delete[] lengthPtr;
lengthPtr = localLen;

std::string *localWid = new std::string;
*localWid = that.check_width();
delete[] widthPtr;
widthPtr = localWid;

return *this;
}

~box() {
std::cout << "User defined destructor called." << '\n';
delete lengthPtr;
delete widthPtr;
}

float check_length () const {
return *lengthPtr;
}

std::string check_width() const{
return *widthPtr;
}

void set_legnth(const float len) {
*lengthPtr = len;
}
void set_width(const std::string str) {
*widthPtr = str;
}

void print_box_info(){
std::cout << *lengthPtr << " " << *widthPtr << '\n';
}
};

我希望能够做的两件主要事情是:
  • 使用 box 将任意数量的我的用户定义类型 ( .push_back() ) 的新元素添加到 vector 中.
  • 存储元素后,我想使用 std::sort 对它们进行排序具有用户定义的比较功能。

  • 这是我用来测试我的 2 个目标的主要功能:
    int main() {
    srand(time(NULL));
    int i = 0;
    std::vector<box> boxes;

    while (i<25) {
    int x = rand()%100+1;
    std::cout<< "x = " << x << '\n';

    if ( i < 5)
    boxes.push_back(box(x, "name"));
    if ( i > 4 && i < 12)
    boxes.push_back(box(x, "Agg"));
    if ( i > 11 && i < 20 )
    boxes.push_back(box(x, "Cragg"));
    if (i>19)
    boxes.push_back(box(x, "Lagg"));

    std::cout << "Added the new box to the collection." << '\n';

    i++;
    }
    for(unsigned int j = 0; j<boxes.size(); j++) {
    boxes[j].print_box_info();
    }
    std::sort(boxes.begin(), boxes.end(), type_is_less);
    }

    到目前为止,我编写的代码似乎能够完成目标 1。运行程序后,while 循环之后的 for 循环打印存储在我的框 vector 中的 25 个框的信息。但是,当我尝试使用 std::sort 对我的盒子进行排序时, 和 type_is_less()功能:
    bool type_is_less(const box &a, const box &b) {
    std::cout<<"In type is less." << '\n';
    std::string A = a.check_width();
    std::string B = b.check_width();

    std::cout<< "Comparing box a, width = " << A << '\n';
    std::cout<< "with box b, width = " << B << '\n';
    bool val = A<B;
    std::cout << "Returning " << val <<'\n' <<'\n';
    return A<B;
    }

    我得到一个段错误,但我不确定错误来自哪里。用户定义的复制构造函数似乎是 seg 错误发生之前调用的最终函数。似乎复制构造函数在 push_back() 中可用,但它会导致 std::sort 中的问题?

    我尝试使用 std::cout 调试复制构造函数每行之间的消息,并且复制构造函数的每一行似乎都在执行而不会导致段错误。一旦复制构造函数完成执行,seg 错误似乎就会出现。我的控制台输出的尾部如下(//我使用'//'插入了注释):

    Added the new box to the collection.

    3 name

    //...

    //...

    // program prints the 2 info points for each box

    61 Lagg // This is the final box info print.

    In type is less. Comparing box a, width = name with box b, width = Cragg Returning 0

    In type is less. Comparing box a, width = name with box b, width = Lagg Returning 0

    In type is less. Comparing box a, width = Cragg with box b, width = Lagg Returning 1

    User defined copy constructor called

    Segmentation fault (core dumped)



    这里有一些移动部分,我不确定如何找出我的代码的哪一部分行为不正确。一切似乎都指向用户定义的复制构造函数是罪魁祸首,但我不确定如何调整它。任何建议将不胜感激。

    我尚未调查的一个悬而未决的问题是我是否可以定义一个与此类似的类,但使用非指针变量 lengthPtr , 和 widthPtr ,并且仍然具有相同的功能。

    最佳答案

    实际上,您的 box类不需要使用任何指针,因为成员可以只是非指针类型。

    但是让我们假设您这样做是出于实验目的:您的 box赋值运算符有几个问题:

  • delete... 的错误形式的使用(应该是 delete ,而不是 delete[] )。
  • 没有检查 box 的自分配实例。
  • 如果 new std::string 有问题抛出一个异常,你已经通过更改 lengthPtr 破坏了你的对象。 .


  • 对于 1),修复很简单,并且给定您的测试程序,将解决崩溃问题:
      box& operator=(const box& that) {
    std::cout << "Copy assignment operator called";

    float* localLen = new float;
    *localLen = that.check_length();
    delete lengthPtr; // Correct form of `delete`
    lengthPtr = localLen;

    std::string* localWid = new std::string;
    *localWid = that.check_width();
    delete widthPtr; // Correct form of `delete`
    widthPtr = localWid;

    return *this;
    }

    但是,如果 box 的自赋值,您的代码将导致未定义的行为。目标是要完成的。

    对于 2),在尝试重新创建拷贝之前需要进行检查:
      box& operator=(const box& that) 
    {
    std::cout << "Copy assignment operator called";

    // check if attempting to assign to myself. If so, just return
    if ( &that == this )
    return *this;

    float* localLen = new float;
    *localLen = that.check_length();
    delete lengthPtr; // Correct form of `delete`
    lengthPtr = localLen;

    std::string* localWid = new std::string;
    *localWid = that.check_width();
    delete widthPtr; // Correct form of `delete`
    widthPtr = localWid;

    return *this;
    }

    对于 3),请注意使用 new ,有可能(即使是远程的) new throw std::bad_alloc异常(exception)。如果发生这种情况,并且它发生在 new std::string行,你会损坏你的 box对象,因为 lengthPtr被提前改变了。

    同样,您的示例将是非常罕见的 new失败,但如果我们要分配几百万 std::string,则可能会发生相同的情况正在使用对 new std::string [x] 的一次调用.

    为避免因动态内存分配失败而损坏对象,您应该在对对象本身进行任何更改之前预先分配所有需要的内存,并检查每个分配(第一个分配除外)是否引发异常。然后,如果抛出异常,您必须回滚之前成功分配的内存。

    这是一个例子:
    box& operator=(const box& that) 
    {
    std::cout << "Copy assignment operator called";
    if ( &that == this )
    return *this;

    // Allocate everything first
    float* localLen = new float; // If this throws, we'll exit anyway. No harm
    std::string* localWid = nullptr;
    try
    {
    localWid = new std::string; // If this throws exception, need to rollback previous allocation and get out
    }
    catch (std::bad_alloc& e)
    {
    delete localLen; // rollback previous allocation and rethrow
    throw e;
    }

    // Everything is ok, now make changes
    *localLen = that.check_length();
    delete lengthPtr;
    delete widthPtr;
    lengthPtr = localLen;
    widthPtr = localWid;
    return *this;
    }

    总的来说,对于一个正确工作的赋值运算符来说,这是很多工作。

    好消息是有一种更容易编写代码的技术可以解决所有提到的问题,只要你有一个有效的复制构造函数和析构函数。该技术是 copy / swap idiom :
     box& operator=(const box& that) 
    {
    std::cout << "Copy assignment operator called";
    box temp(that);
    std::swap(lengthPtr, temp.lengthPtr);
    std::swap(widthPtr, temp.widthPtr);
    return *this;
    }

    不需要自赋值检查(即使它可以用于优化目的),也不需要检查 new抛出,因为没有调用 new真的完成了(如果出现问题, temp 的创建会自动将我们排除在外)。

    关于c++ - 使用带有 std::vector 和 std::sort 的用户定义类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60042198/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com