gpt4 book ai didi

C++ 我应该使用指针还是引用?

转载 作者:行者123 更新时间:2023-11-28 05:56:30 26 4
gpt4 key购买 nike

我想深入了解何时应该使用引用或指针。

让我们以使用 Rectangle 类作为其内部边界框的 Polygon 类为例。

多边形.h

class Polygon {

private:
std::list<Point> _points;
Rectangle _boundingBox;

public:
Polygon(const std::list<Point> &);

public:
const std::list<Point> &getPoints() const;
const Rectangle &getBoundingBox() const;

private:
void setBoundingBox();

};

多边形.cpp

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

Polygon::Polygon(const std::list<Point> &points)
{
if (points.size() < polygon::MIN_SIDE + 1) {
throw std::invalid_argument("A polygon is composed of at least 3 sides.");
}

if (points.front() != points.back()) {
throw std::invalid_argument("A polygon must be closed therefore the first point must be equal to the last one.");
}

std::list<Point>::const_iterator it;
for (it = ++points.begin(); it != points.end(); ++it) {
this->_points.push_back(*it);
}
this->setBoundingBox();
}

void Polygon::translate(const std::array<float, 2> &vector)
{
std::list<Point>::iterator it;
for (it = this->_points.begin(); it != this->_points.end(); ++it) {
(*it).setX((*it).getX() + vector[0]);
(*it).setY((*it).getY() + vector[1]);
}
Point topLeft = this->_boundingBox->getTopLeft();
Point bottomRight = this->_boundingBox->getBottomRight();

topLeft.setX(topLeft.getX() + vector[0]);
topLeft.setY(topLeft.getY() + vector[1]);
bottomRight.setX(bottomRight.getX() + vector[0]);
bottomRight.setY(bottomRight.getY() + vector[1]);
}

const std::list<Point> &Polygon::getPoints() const
{
return this->_points;
}

const Rectangle &Polygon::getBoundingBox() const
{
return this->_boundingBox;
}

void Polygon::setBoundingBox()
{
float xMin = this->_points.front().getX();
float xMax = this->_points.front().getX();
float yMin = this->_points.front().getY();
float yMax = this->_points.front().getY();

std::list<Point>::const_iterator it;
for (it = this->_points.begin(); it != this->_points.end(); ++it)
{
Point point = *it;
if (point.getX() < xMin) {
xMin = point.getX();
}
if (point.getX() > xMax) {
xMax = point.getX();
}
if (point.getY() < yMin) {
yMin = point.getY();
}
if (point.getY() > yMax) {
yMax = point.getY();
}
}
this->_boundingBox = new Rectangle(Point(xMin, yMin), Point(xMax, yMax));
}

std::ostream &operator<<(std::ostream &out, const Polygon &polygon)
{
std::list<Point>::const_iterator it;

for (it = polygon.getPoints().begin(); it != polygon.getPoints().end(); ++it) {
out << (*it);
if (it != polygon.getPoints().end()) {
out << " ";
}
}
return out;
}

矩形.h

#pragma once

#include <stdexcept>

#include "Point.h"

class Rectangle {

private:
Point _topLeft;
Point _bottomRight;

public:
Rectangle(const Point &, const Point &);

public:
const Point &getTopLeft() const;
const Point &getBottomRight() const;
float getWidth() const;
float getHeight() const;
};

矩形.cpp

#include "Rectangle.h"

Rectangle::Rectangle(const Point &topLeft, const Point &bottomRight)
{
if (topLeft.getX() > bottomRight.getX() || topLeft.getY() > bottomRight.getY()) {
throw std::invalid_argument("You must specify valid top-left/bottom-right points");
}
this->_topLeft = topLeft;
this->_bottomRight = bottomRight;
}

const Point &Rectangle::getTopLeft() const
{
return this->_topLeft;
}

const Point &Rectangle::getBottomRight() const
{
return this->_bottomRight;
}

float Rectangle::getWidth() const
{
return this->_bottomRight.getX() - this->_topLeft.getX();
}

float Rectangle::getHeight() const
{
return this->_bottomRight.getY() - this->_topLeft.getY();
}

点.h

#pragma once

#include <ostream>
#include <cmath>

class Point {

private:
float _x;
float _y;

public:
Point(float = 0, float = 0);

public:
float distance(const Point &);

public:
float getX() const;
float getY() const;
void setX(float);
void setY(float);
};

std::ostream &operator<<(std::ostream &, const Point &);
bool operator==(const Point &, const Point &);
bool operator!=(const Point &, const Point &);

点.cpp

#include "Point.h"

Point::Point(float x, float y)
{
this->_x = x;
this->_y = y;
}

float Point::distance(const Point &other)
{
return std::sqrt(std::pow(this->_x - other.getX(), 2) + std::pow(this->_y - other.getY(), 2));
}

float Point::getX() const
{
return this->_x;
}

float Point::getY() const
{
return this->_y;
}

void Point::setX(float x)
{
this->_x = x;
}

void Point::setY(float y)
{
this->_y = y;
}

std::ostream &operator<<(std::ostream &out, const Point &point)
{
out << "(" << point.getX() << ", " << point.getY() << ")";
return out;
}

bool operator==(const Point &p1, const Point &p2)
{
return p1.getX() == p2.getX() && p1.getY() == p2.getY();
}

bool operator!=(const Point &p1, const Point &p2)
{
return p1.getX() != p2.getX() || p1.getY() != p2.getY();
}

这段代码带来了很多问题。

  • 这不会编译,因为很明显,每当我们尝试创建多边形时,它最终都会尝试使用不存在的默认构造函数创建矩形。
  • 我不能使用初始化列表,因为边界框显然取决于我的点列表中的一些计算值。
  • 我可以创建默认构造函数,默认情况下为 Rectangle 创建两个 Point(0, 0),但这没有多大意义。
  • 我可以使用指针,但后来我觉得这不是最好的解决方案,因为我倾向于认为这主要用于 C++ 中的多态性,我们应该尽可能使用引用。

我应该如何进行?

我觉得我遗漏了一些有关 C++ 的东西,但可以从中学到很多东西。

最佳答案

我认为您的主要问题是关于如何处理需要同时初始化 std::list<Point> _points; 的问题。和 Rectangle _boundingBox; ,同时还对 _points 进行了一些验证.

最简单的解决方案是只给出 Rectangle默认构造函数(或传递两个默认点作为初始值设定项)。然后,一旦您验证了 points构造函数中的参数,你计算Rectangle基于积分。


一个稍微复杂一点的替代方案是允许从构造函数初始化列表中调用验证函数,例如:

Polygon::Polygon(std::list<Point> points)
: _points( validate_point_list(points), std::move(points) ), _boundingBox( calculateBoundingBox(_points) )
{
}

你有功能的地方(可以是免费功能):

void validate_point_list(std::list<Point> &points)
{
if (points.size() < polygon::MIN_SIDE + 1)
throw std::invalid_argument("A polygon is composed of at least 3 sides.");

if (points.front() != points.back())
throw std::invalid_argument("A polygon must be closed therefore the first point must be equal to the last one.");

// caller must pass in same first and last point, but we only store one of the two
points.erase( points.begin() );
}

Rectangle calculateBoundingBox(std::list<Point> const &_points)
{
// whatever logic you have in setBoundingBox, except return the answer
}

请注意,您的 Polygon 构造函数中的循环不必要地复杂。你可以只写 _points = points;然后删除额外的点(对于列表来说是 O(1))。


请注意,我已经按值传递,然后使用 std::move .原因是如果给定的参数是一个右值,那么它可以直接移动到它被存储的地方;而对于 const &版本,存储拷贝,然后销毁原始版本。

我会使用 const &比你做的少很多。小对象,例如 PointRectangle ,不会因按值传递而受到性能损失(甚至可能更有效率)。并且如上一段所述;如果您的函数接受一个参数并且它将获取该参数的拷贝,则最好按值传递。

仅当您使用但不存储传递的值时,通过引用传递才是最好的。例如,calculateBoundingBox .


最后,一旦你完成这项工作,你可能想要考虑让 Polygon 构造函数接受点范围的迭代器对,和/或 std::initializer_list .

关于C++ 我应该使用指针还是引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34077959/

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