复制包含unique_ptrs

时间:2015-06-08 04:58:05

标签: c++ smart-pointers unique-ptr

我正在使用C ++和Allegro开发一款简单的游戏。我遇到Access violation运行时错误,其中vector structs unique_ptrs包含ALLEGRO_BITMAPsstruct Skin { std::unique_ptr<ALLEGRO_BITMAP> img; Skin(); Skin(ALLEGRO_BITMAP*); Skin& operator=(const Skin& s); Skin(const Skin& s); };

这是我的结构声明。

Skin::Skin() {
    img.reset();
}

Skin::Skin(ALLEGRO_BITMAP* bitmap) {
    img.reset(bitmap);
}

Skin::Skin(const Skin& s) {
    img.reset(s.img.get());
}

Skin& Skin::operator=(const Skin& s) {
    img.reset(s.img.get());
    return *this;
}

以下是另一个文件中构造函数的定义。

generateBase(world, display.get());

以下是在我的访问冲突之前调用的代码。

void generateBase(World& world, ALLEGRO_DISPLAY* display) {
    int x = TILESIZE - WIDTH;
    int y = HEIGHT - TILESIZE;
    int groundWidth = 3 * WIDTH - 2 * TILESIZE;
    Point min{ x, y };
    Point max{ x + groundWidth, y + (int)TILESIZE };
    ALLEGRO_BITMAP* black = al_create_bitmap(groundWidth, TILESIZE);
    ALLEGRO_BITMAP* white = al_create_bitmap(groundWidth, TILESIZE);
    al_set_target_bitmap(black);
    al_clear_to_color(al_map_rgb(0, 0, 0));
    al_set_target_bitmap(white);
    al_clear_to_color(al_map_rgb(255, 255, 255));
    al_set_target_bitmap(al_get_backbuffer(display));
    std::cout << "Errors incoming!" << endl;
    createPlayer(world, x, y, 0, 0, 5, vector < AABB > { AABB(min, max) }, vector < Skin > { Skin(black), Skin(white) });
    std::cout << "Did we make it?" << endl;
}

调用此功能。

unsigned int createPlayer(World& world, int x, int y, float dx, float dy, float speed, vector<AABB>& mesh, vector<Skin>& imgs) {
    unsigned int entity = newEntityIndex(world);
    world.masks[entity].set(COMPONENT_TYPE);
    world.masks[entity].set(COMPONENT_POINT);
    world.masks[entity].set(COMPONENT_UNITVECTOR);
    world.masks[entity].set(COMPONENT_SPEED);
    world.masks[entity].set(COMPONENT_COLLISIONMESH);
    world.masks[entity].set(COMPONENT_SKINLIST);
    world.types[entity] = TYPE_PLAYER;
    world.points[entity] = Point(x, y);
    world.unitVectors[entity] = UnitVector(dx, dy);
    world.speeds[entity] = Speed(speed);
    world.collisionMeshes[entity].mesh = mesh;
    cout << "Starting vector copy" << endl;
    for (auto skin : imgs) {
        world.skinLists[entity].imgs.push_back(move(skin));
    }
    cout << "Ending vector copy" << endl;
    return entity;
}

反过来调用此功能。

namespace std {
    template<>
    class default_delete < ALLEGRO_BITMAP > {
    public:
        void operator()(ALLEGRO_BITMAP* ptr) {
            cout << ptr << endl;
            al_destroy_bitmap(ptr);
        }
    };
}

这是我对unique_ptr的删除。

Errors incoming!
Starting vector copy
00AF9468
00AF9468

这是输出。

createPlayer

当我通过删除generateBase修改Skin(white)中的Errors incoming! Starting vector copy 00799468 Ending vector copy 00799468 来电时,输出更改为。

vector

输出的变化让我感到有些困惑,但我最大的问题是我需要改变一些关于如何复制structs unique_ptrs package com.pluralsight.model; import hello.User; import java.util.ArrayList; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.Table; import org.hibernate.validator.constraints.Range; import org.springframework.web.bind.annotation.ModelAttribute; @Entity @Table(name="goals") public class Goal { public static final String FIND_ALL_GOALS = "findALLGoals"; public static final String FIND_GOAL_REPORTS = "findGoalReports"; @Id @GeneratedValue @Column(name="GOAL_ID") private Long id; @Range(min = 1, max = 120) @Column(name="MINUTES") private int minutes; // @OneToMany(mappedBy="goal",cascade=CascadeType.ALL, fetch=FetchType.LAZY) // private List<Exercise> exercises = new ArrayList<Exercise>(); @ManyToOne @JoinColumn(name="USER_NAME") private User user; // // public List<Exercise> getExercises() { // return exercises; // } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public Long getId() { return id; } public int getMinutes() { return minutes; } // public void setExercises(List<Exercise> exercises) { // this.exercises = exercises; // } public void setId(Long id) { this.id = id; } public void setMinutes(int minutes) { this.minutes = minutes; } } package com.pluralsight.controller; import java.util.List; import javax.servlet.http.HttpSession; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.SessionAttributes; import com.pluralsight.model.Goal; import com.pluralsight.model.GoalReport; import com.pluralsight.model.User; import com.pluralsight.service.GoalService; @Controller @SessionAttributes("goal") public class GoalController { @Autowired private GoalService goalService; @RequestMapping(value = "addGoal", method = RequestMethod.GET) public String addGoal(Model model, HttpSession session ) { //Goal goal = new Goal(); Goal goal = (Goal)session.getAttribute("goal"); if(goal == null){ goal = new Goal(); goal.setMinutes(10); } model.addAttribute("goal", goal); return "addGoal"; } @RequestMapping(value = "addGoal", method = RequestMethod.POST) public String updateGoal(@Valid @ModelAttribute Goal goal, BindingResult result) { System.out.println("result has errors: " + result.hasErrors()); System.out.println("Goal set: " + goal.getMinutes()); if(result.hasErrors()) { return "addGoal"; }else{ goalService.save(goal); } return "redirect:index.jsp"; } @RequestMapping(value="getGoals", method= RequestMethod.GET) public String getGoals(Model model){ List<Goal> goals = goalService.findAllGoals(); model.addAttribute("goals", goals); return "getGoals"; } @RequestMapping(value="getGoalReports", method= RequestMethod.GET) public String getGoalReports(Model model){ List<GoalReport> goalReports = goalService.findAllGoalReports(); model.addAttribute("goalReports", goalReports); return "getGoalReports"; } }的问题,以便我不要不要尝试两次删除相同的指针。

提前致谢!

2 个答案:

答案 0 :(得分:3)

首先要理解的是,您只能有一个包含指向特定对象的指针的std::unique_ptr对象。您的Skin(const Skin& s)构造函数违反了此原则,导致unique_ptr的两个副本。如果您的对象包含unique_ptr个成员,则需要执行以下操作之一:

  1. 没有复制构造函数或赋值运算符。
  2. 在复制构造函数和/或赋值运算符中,分配基础资源的新副本。这需要调用al_clone_bitmap来复制资源。
  3. 其次,当您在unique_ptr中持有资源时,您希望在创建资源的同一位置初始化unique_ptr。例如,不要创建局部变量ALLEGRO_BITMAP* black,而是使用以下内容:

    std::unique_ptr<ALLEGRO_BITMAP> black(al_create_bitmap(groundWidth, TILESIZE));
    

    由于此代码直接从unique_ptr的结果创建al_create_bitmap,因此您需要删除 Skin构造函数,其中包含ALLEGRO_BITMAP* 1}}并将其替换为:

    Skin::Skin(std::unique_ptr<ALLEGRO_BITMAP>&& bitmap)
        : img(bitmap)
    {
    }
    

    然后,您可以将Skin移入其中来创建unique_ptr

    Skin(std::move(black))
    

    将上述内容放在一起,工作副本构造函数可能如下所示。它不是特别有效,但它是安全的。

    Skin::Skin(const Skin& s)
        : img(al_clone_bitmap(s.img.get()))
    {
    }
    

答案 1 :(得分:1)

问题在于:

Skin::Skin(const Skin& s) {
    img.reset(s.img.get());
}

Skin& Skin::operator=(const Skin& s) {
    img.reset(s.img.get());

您正在从一个unique_ptr窃取原始指针并将其分配给另一个。现在unique_ptr属于RAII类别。只要它们还活着,它们就希望拥有物体的寿命。 当你这样做时

img.reset(s.img.get());

你从一个unique_ptr中取出指针并移交给另一个unique_ptr。现在,unique_ptr1认为它拥有底层对象而不知道还有另一个相同的unique_ptr2。因此,当他们死亡时,他们将愉快地释放为_Ptr分配的内存。因此,您的代码必然会最终访问/释放已经被第一个unique_ptr消失的内存。 Now both unique_ptr believes that they own _Ptr

您必须移动unique_ptr(从而产生所有权)或在s.img上显式调用release