目录
1.insideTriangle函数——判断点(x,y)是否在三角形内
empalce_front/emplace/emplace_back
提供的代码框架里包含了三个头文件和三个源文件
补充作业代码
需要补充的部分有:
main.cpp
get_projection_matrix函数
注意,与作业1情况类似,如果n和f直接取zNear和zFar则两个三角形会是倒三角。
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
// TODO: Copy-paste your implementation from the previous assignment.
Eigen::Matrix4f projection;
float f, n, l, r, b, t, fov;
fov = eye_fov / 180 * MY_PI;
n = -zNear; //znear是正值
f = -zFar;
t = tan(fov / 2) * zNear;
b = -t;
r = t * aspect_ratio;
l = -r;
//透视->正交 perspective->orthographic
Eigen::Matrix4f pertoorth;
pertoorth << n, 0, 0, 0,
0, n, 0, 0,
0, 0, n + f, -n * f,
0, 0, 1, 0;
//正交——移动
Eigen::Matrix4f orth1;
orth1 <<
1, 0, 0, -(r + l) / 2,
0, 1, 0, -(t + b) / 2,
0, 0, 1, -(n + f) / 2,
0, 0, 0, 1;
//正交——缩放
Eigen::Matrix4f orth2;
orth2 <<
2 / (r - l), 0, 0, 0,
0, 2 / (t - b), 0, 0,
0, 0, 2 / (zFar - zNear), 0,
0, 0, 0, 1;
projection = orth2 * orth1 * pertoorth;//注意矩阵顺序,变换从右往左依次进行
return projection;
}
rasterize.cpp
1.insideTriangle函数——判断点(x,y)是否在三角形内
这个部分重点是在abc三个取值的问题,注意是:x1y2-x2y1
static bool insideTriangle(int x, int y, const Vector3f* _v)//第三个数是_v指向Vector3f类型的指针,默认传入了三角形的三个顶点
{
//.head(2)用来取出每个顶点的前两个值(x,y)
Eigen::Vector2f AB, BC, CA, AP, BP, CP, p;
float a, b, c;
p << x, y;
AB = _v[1].head(2) - _v[0].head(2);
AP = p - _v[0].head(2);
BC = _v[2].head(2) - _v[1].head(2);
BP = p - _v[1].head(2);
CA = _v[0].head(2) - _v[2].head(2);
CP = p - _v[2].head(2);
a = AB[0] * AP[1] - AB[1] * AP[0];
b = BC[0] * BP[1] - BC[1] * BP[0];
c = CA[0] * CP[1] - CA[1] * CP[0];
if (a > 0 && b > 0 && c > 0) return true;
else if (a < 0 && b < 0 && c < 0) return true;
return false;
}
下面这个是在作业3里老师给的框架里的方法,比我的简单多了:
static bool insideTriangle(int x, int y, const Vector4f* _v){
Vector3f v[3];
for(int i=0;i<3;i++)
v[i] = {_v[i].x(),_v[i].y(), 1.0};
Vector3f f0,f1,f2;
f0 = v[1].cross(v[0]);
f1 = v[2].cross(v[1]);
f2 = v[0].cross(v[2]);
Vector3f p(x,y,1.);
if((p.dot(f0)*f0.dot(v[2])>0) && (p.dot(f1)*f1.dot(v[0])>0) && (p.dot(f2)*f2.dot(v[1])>0))
return true;
return false;
}
2.rasterizer_triangle
这个部分一个取bounding box问题,一个是重心坐标的问题。
void rst::rasterizer::rasterize_triangle(const Triangle& t) { //这里的Triangle是Triangle.hpp里定义的一个class类
auto v = t.toVector4(); //toVector4()是Triangle里定义的一个array<vector4f,3>
//找到三角形的bounding box
//在Triangle类中定义了三角形顶点为v[0]v[1]v[2] 都是vector4f
int min_x = std::min(std::min(v[0].x(), v[1].x()), v[2].x());
int min_y = std::min(std::min(v[0].y(), v[1].y()), v[2].y());
int max_x = std::max(std::max(v[0].x(), v[1].x()), v[2].x());
int max_y = std::max(std::max(v[0].y(), v[1].y()), v[2].y());
//遍历pixels确定像素是否在三角形内
/*bool MSAA = true;
这里用MSAA 4X 也就是把像素分成4X4,默认像素是1X1的大小,因此有了0.25、0.75
std::vector<Eigen::Vector2f>pixel
{
{0.25,0.25},{0.25,0.75},{0.75,0.25},{0.75,0.75},
};
*/
//循环判断
for (int x = min_x; x <= max_x; x++) {
for (int y = min_y; y <= max_y; y++) {
//判断是否在三角形内
if (insideTriangle(x + 0.5, y + 0.5, t.v)){
//在三角形内,用下述code取得插值的z值
auto[alpha,beta,gamma]=computeBarycentric2D(x + 0.5, y + 0.5, t.v);//为了获得该点的z值
float w_reciprocal = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
z_interpolated *= w_reciprocal;
int cur_index = get_index(x, y);
if(z_interpolated<depth_buf[cur_index])
{
depth_buf[cur_index] = z_interpolated;
Vector3f vertex;
vertex << x, y, z_interpolated;
set_pixel(vertex, t.getColor());
}
}
}
}
}
结果展示
正三角-n和f取负值
倒三角-n和f取正值
代码框架详细理解
做作业不局限于把框架填完整,要把整个框架理解清楚。
Triangle.hpp
//预处理-宏定义
#ifndef RASTERIZER_TRIANGLE_H //条件编译,判断是否被定义过
#define RASTERIZER_TRIANGLE_H //若没有被定义,则进行宏定义
#include <Eigen> //预处理-文件包含命令
using namespace Eigen; //使用命名空间Eigen
class Triangle{//定义了一个名为Triangle的
public:
Vector3f v[3]; //按照逆时针顺序定义三角形三个顶点坐标
//定义三角形每个顶点的属性
Vector3f color[3]; //每个顶点的颜色
Vector2f tex_coords[3]; //顶点的(u,v)坐标
Vector3f normal[3]; //每个顶点的法线
//Texture *tex;
Triangle();
void setVertex(int ind, Vector3f ver); /*set i-th vertex coordinates */
void setNormal(int ind, Vector3f n); /*set i-th vertex normal vector*/
void setColor(int ind, float r, float g, float b); /*set i-th vertex color*/
Vector3f getColor() const { return color[0]*255; } // Only one color per triangle.
void setTexCoord(int ind, float s, float t); /*set i-th vertex texture coordinate*/
std::array<Vector4f, 3> toVector4() const;//std::array是用来封装固定大小数组的容器,大小必须在编译期确定
};
#endif //RASTERIZER_TRIANGLE_H
rasterizer.hpp
#pragma once //防止重复引用,与#ifndef作用相同
#include <Eigen> //引入Eigen库
#include <algorithm> //<algrithm>定义了C++STL标准中的基础性算法,均为函数模板
#include "global.hpp"
#include "Triangle.hpp"
using namespace Eigen; //使用Eigen命名空间,之后就不用写Eigen::了
namespace rst //创建一个命名空间rst
{
enum class Buffers //定义枚举类Buffers
{
Color = 1,
Depth = 2
};
inline Buffers operator|(Buffers a, Buffers b) //operator| 重载非短路或运算
{
return Buffers((int)a | (int)b);
}
inline Buffers operator&(Buffers a, Buffers b) //operator& 重载非短路与运算
{
return Buffers((int)a & (int)b);
}
enum class Primitive //同样,定义一个枚举类primitive
{
Line,
Triangle
};
//为确保安全:draw function用了两个buffer id进行讨论,防止用户搞错前后顺序,如果顺序错了编译器就不会进行这段程序。
//声明三个结构体,sturct默认公有继承,默认是public的
struct pos_buf_id //position位置
{
int pos_id = 0;
};
struct ind_buf_id //
{
int ind_id = 0;
};
struct col_buf_id //颜色
{
int col_id = 0;
};
class rasterizer //声明一个类
{
public: //公开部分
rasterizer(int w, int h);
pos_buf_id load_positions(const std::vector<Eigen::Vector3f>& positions);
ind_buf_id load_indices(const std::vector<Eigen::Vector3i>& indices);
col_buf_id load_colors(const std::vector<Eigen::Vector3f>& colors);
void set_model(const Eigen::Matrix4f& m);
void set_view(const Eigen::Matrix4f& v);
void set_projection(const Eigen::Matrix4f& p);
void set_pixel(const Eigen::Vector3f& point, const Eigen::Vector3f& color);
void clear(Buffers buff);
void draw(pos_buf_id pos_buffer, ind_buf_id ind_buffer, col_buf_id col_buffer, Primitive type);
std::vector<Eigen::Vector3f>& frame_buffer() { return frame_buf; }
private:
void draw_line(Eigen::Vector3f begin, Eigen::Vector3f end);
void rasterize_triangle(const Triangle& t);
// VERTEX SHADER -> MVP -> Clipping -> /.W -> VIEWPORT -> DRAWLINE/DRAWTRI -> FRAGSHADER
private:
Eigen::Matrix4f model;
Eigen::Matrix4f view;
Eigen::Matrix4f projection;
std::map<int, std::vector<Eigen::Vector3f>> pos_buf;
std::map<int, std::vector<Eigen::Vector3i>> ind_buf;
std::map<int, std::vector<Eigen::Vector3f>> col_buf;
std::vector<Eigen::Vector3f> frame_buf;
std::vector<float> depth_buf;
int get_index(int x, int y);
int width, height;
int next_id = 0;
int get_next_id() { return next_id++; }
};
}
rasterizer.cpp
#include <algorithm> //是C++标准算法库,应用在容器上
#include <vector> //是c++标准库提供的一个被封装的动态数组
#include "rasterizer.hpp"
#include <opencv2/opencv.hpp>
#include <math.h>
/*这里的rst是rasterizer.hpp头文件中定义的一个rst namespace
pos_buf_id 是rst中定义的一个 struct
rasterizer 是rst中定义的一个 class
*/
rst::pos_buf_id rst::rasterizer::load_positions(const std::vector<Eigen::Vector3f> &positions)
{
auto id = get_next_id();//rasterizer类中定义的一个int类型的函数,作用:return下一个id
pos_buf.emplace(id, positions);//在id指向的元素前创建一个值为positions的元素
return {id};
}
rst::ind_buf_id rst::rasterizer::load_indices(const std::vector<Eigen::Vector3i> &indices)
{
auto id = get_next_id();
ind_buf.emplace(id, indices);
return {id};
}
rst::col_buf_id rst::rasterizer::load_colors(const std::vector<Eigen::Vector3f> &cols)
{
auto id = get_next_id();
col_buf.emplace(id, cols);
return {id};
}
auto to_vec4(const Eigen::Vector3f& v3, float w = 1.0f)
{
return Vector4f(v3.x(), v3.y(), v3.z(), w);
}
static bool insideTriangle(int x, int y, const Vector3f* _v)//第三个数是_v指向Vector3f类型的指针,默认传入了三角形的三个顶点
{
//.head(2)用来取出每个顶点的前两个值(x,y)
Eigen::Vector2f AB, BC, CA, AP, BP, CP, p;
float a, b, c;
p << x, y;
AB = _v[1].head(2) - _v[0].head(2);
AP = p - _v[0].head(2);
BC = _v[2].head(2) - _v[1].head(2);
BP = p - _v[1].head(2);
CA = _v[0].head(2) - _v[2].head(2);
CP = p - _v[2].head(2);
a = AB[0] * AP[1] - AB[1] * AP[0];
b = BC[0] * BP[1] - BC[1] * BP[0];
c = CA[0] * CP[1] - CA[1] * CP[0];
if (a > 0 && b > 0 && c > 0) return true;
else if (a < 0 && b < 0 && c < 0) return true;
return false;
}
static std::tuple<float, float, float> computeBarycentric2D(float x, float y, const Vector3f* v)
{
float c1 = (x*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*y + v[1].x()*v[2].y() - v[2].x()*v[1].y()) / (v[0].x()*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*v[0].y() + v[1].x()*v[2].y() - v[2].x()*v[1].y());
float c2 = (x*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*y + v[2].x()*v[0].y() - v[0].x()*v[2].y()) / (v[1].x()*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*v[1].y() + v[2].x()*v[0].y() - v[0].x()*v[2].y());
float c3 = (x*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*y + v[0].x()*v[1].y() - v[1].x()*v[0].y()) / (v[2].x()*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*v[2].y() + v[0].x()*v[1].y() - v[1].x()*v[0].y());
return {c1,c2,c3};
}
void rst::rasterizer::draw(pos_buf_id pos_buffer, ind_buf_id ind_buffer, col_buf_id col_buffer, Primitive type)
{
auto& buf = pos_buf[pos_buffer.pos_id];
auto& ind = ind_buf[ind_buffer.ind_id];
auto& col = col_buf[col_buffer.col_id];
float f1 = (50 - 0.1) / 2.0;
float f2 = (50 + 0.1) / 2.0;
Eigen::Matrix4f mvp = projection * view * model;
for (auto& i : ind)//用i遍历ind
{
Triangle t;
Eigen::Vector4f v[] = {
mvp * to_vec4(buf[i[0]], 1.0f),
mvp * to_vec4(buf[i[1]], 1.0f),
mvp * to_vec4(buf[i[2]], 1.0f)
};
//Homogeneous division
for (auto& vec : v) {
vec /= vec.w();
}
//Viewport transformation
for (auto & vert : v)
{
vert.x() = 0.5*width*(vert.x()+1.0);
vert.y() = 0.5*height*(vert.y()+1.0);
vert.z() = vert.z() * f1 + f2;
}
for (int i = 0; i < 3; ++i)
{
t.setVertex(i, v[i].head<3>());
t.setVertex(i, v[i].head<3>());
t.setVertex(i, v[i].head<3>());
}
auto col_x = col[i[0]];
auto col_y = col[i[1]];
auto col_z = col[i[2]];
t.setColor(0, col_x[0], col_x[1], col_x[2]);
t.setColor(1, col_y[0], col_y[1], col_y[2]);
t.setColor(2, col_z[0], col_z[1], col_z[2]);
rasterize_triangle(t);
}
}
//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t) { //这里的Triangle是Triangle.hpp里定义的一个class类
auto v = t.toVector4(); //toVector4()是Triangle里定义的一个array<vector4f,3>
//找到三角形的bounding box
//在Triangle类中定义了三角形顶点为v[0]v[1]v[2] 都是vector4f
int min_x = std::min(std::min(v[0].x(), v[1].x()), v[2].x());
int min_y = std::min(std::min(v[0].y(), v[1].y()), v[2].y());
int max_x = std::max(std::max(v[0].x(), v[1].x()), v[2].x());
int max_y = std::max(std::max(v[0].y(), v[1].y()), v[2].y());
//遍历pixels确定像素是否在三角形内
//循环判断
for (int x = min_x; x <= max_x; x++) {
for (int y = min_y; y <= max_y; y++) {
//判断是否在三角形内
if (insideTriangle(x + 0.5, y + 0.5, t.v)){
//在三角形内,用下述code取得插值的z值
auto[alpha,beta,gamma]=computeBarycentric2D(x + 0.5, y + 0.5, t.v);//为了获得该点的z值
float w_reciprocal = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
z_interpolated *= w_reciprocal;
int cur_index = get_index(x, y);
if(z_interpolated<depth_buf[cur_index])
{
depth_buf[cur_index] = z_interpolated;
Vector3f vertex;
vertex << x, y, z_interpolated;
set_pixel(vertex, t.getColor());
}
}
}
}
}
void rst::rasterizer::set_model(const Eigen::Matrix4f& m)
{
model = m;
}
void rst::rasterizer::set_view(const Eigen::Matrix4f& v)
{
view = v;
}
void rst::rasterizer::set_projection(const Eigen::Matrix4f& p)
{
projection = p;
}
void rst::rasterizer::clear(rst::Buffers buff)
{
if ((buff & rst::Buffers::Color) == rst::Buffers::Color)
{
std::fill(frame_buf.begin(), frame_buf.end(), Eigen::Vector3f{0, 0, 0});
}
if ((buff & rst::Buffers::Depth) == rst::Buffers::Depth)
{
std::fill(depth_buf.begin(), depth_buf.end(), std::numeric_limits<float>::infinity());
}
}
rst::rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
frame_buf.resize(w * h);
depth_buf.resize(w * h);
}
int rst::rasterizer::get_index(int x, int y)
{
return (height-1-y)*width + x;
}
void rst::rasterizer::set_pixel(const Eigen::Vector3f& point, const Eigen::Vector3f& color)
{
//old index: auto ind = point.y() + point.x() * width;
auto ind = (height-1-point.y())*width + point.x();
frame_buf[ind] = color;
}
// clang-format on
main.cpp
main其实跟作业1的差不多,这里就不粘贴解释了。
Triangle.cpp
#include "Triangle.hpp"
#include <algorithm>
#include <array>
Triangle::Triangle() {//Triangle头文件中定义了类Triangle,因此可以使用域操作符::表示Triangle()是类Triangle的成员函数
v[0] << 0,0,0;
v[1] << 0,0,0;
v[2] << 0,0,0;
color[0] << 0.0, 0.0, 0.0;
color[1] << 0.0, 0.0, 0.0;
color[2] << 0.0, 0.0, 0.0;
tex_coords[0] << 0.0, 0.0;
tex_coords[1] << 0.0, 0.0;
tex_coords[2] << 0.0, 0.0;
}
void Triangle::setVertex(int ind, Vector3f ver){
v[ind] = ver;
}
void Triangle::setNormal(int ind, Vector3f n){
normal[ind] = n;
}
void Triangle::setColor(int ind, float r, float g, float b) {
if((r<0.0) || (r>255.) ||
(g<0.0) || (g>255.) ||
(b<0.0) || (b>255.)) {
fprintf(stderr, "ERROR! Invalid color values");
fflush(stderr);
exit(-1);
}
color[ind] = Vector3f((float)r/255.,(float)g/255.,(float)b/255.);
return;
}
void Triangle::setTexCoord(int ind, float s, float t) {
tex_coords[ind] = Vector2f(s,t);
}
std::array<Vector4f, 3> Triangle::toVector4() const
{
std::array<Eigen::Vector4f, 3> res;
std::transform(std::begin(v), std::end(v), res.begin(), [](auto& vec) { return Eigen::Vector4f(vec.x(), vec.y(), vec.z(), 1.f); });
return res;//返回的是一个数组,3个参数,每个参数:
}
代码涉及到的c++知识回顾
宏定义
参考:宏定义#define #ifndef #endif_马小超i的博客-CSDN博客
#define #ifndef #endif 宏”分为有参数和无参数两种。其中#表示这是一条预处理命令,凡是#开头的都是预处理命令,预处理命令分为三种:宏定义、文件包含、条件编译
无参宏定义
一般表示为#define 标识符 字符串
“define为宏定义命令;“标识符”是定义的宏名;“字符串”可以是常数、表达式、格式串等,要终止其作用域可以使用#undef。
#include<iostream>
#define M (a+b)
using namespace std;
int main() {
int sum, a, b;
cout << "input a b:";
cin >> a >> b;
sum = M * M;
cout << sum << endl;
return 0;
}
带参宏定义
#define 宏名(形参表) 字符串
调用的时候要用实参去替换形参。
#include<iostream>
#define plus(x) ++x
using namespace std;
int main() {
int a;
cout << "input a:";
cin >> a;
cout << plus(a) << endl;
return 0;
}
#ifndef条件编译
#表示预处理命令;ifndef是if not define的缩写,是与处理功能的条件编译,目的是防止头文件的重复包含和编译。
在c++中用#ifndef可以避免出现以下错误:在.hpp头文件中定义了全局变量,一个.cpp文件中多次包含这个.hpp文件,加上#ifndef可以避免重复定义的错误。
#ifndef x //判断x是否已经被宏定义
#define x
程序段1 //如果x没被定义,定义x,并编译程序段1
#endif
程序段2 //如果x已经被定义,忽略1编译2
#pragma once
除了用#ifndef防止h文件的代码重复引用,还可以用#pragma once指令,在想要保护的文件开头写入
#pragma once
相比#ifndef(灵活,兼容性好),#pragma once编译时间相对较短,操作简单相率高;是一个非标准但被广泛支持的方式。
using namespace
using
C++11以后,using也可以用来像typedef一样定义类型别名,可读性更强,可以像定义一个变量一样给一个类型定义别名。
namespace 命名空间
namespace+命名空间的名字
相当于一个文件夹,把需要的变量、对象、函数等包含进去,后续可以直接rst::或using namespace 来使用。每个命名空间都有自己的作用域。
同时C++11新标准引入了内联命名空间(在namespace前加上inline)可以直接被外层的命名空间直接使用。
namespace myName{
//相关定义及声明
}
如何访问命名空间?
(1)myName:: 标识符
日常写代码用到的cout/cin/endl等等都是C++的标识符
#include <iostream>
int main()
{
std::cout<<"hello world!"<<std::endl;
}
(2)using namespace myName;
在头文件后都会加上using namespace std;这样可以直接用cout等标识符了,很方便。
#include <iostream>
using namespace std;
int main()
{
cout<<"hello world!"<<endl;
}
class 类
class声明类似struct,基本结构是:
class 类名
{
public:
//行为或属性
private:
//行为或属性
};
其中:public/private为限制行为/属性的关键字,public表示内容是公开的,可以被外界(例如main等)公开访问也可以被class类自己访问;private则表示内容是私密的,只能在内部调用,不能外部访问。结束末尾有分号;
class Triangle{
public:
Vector3f v[3];
Vector3f color[3];
Vector2f tex_coords[3];
Vector3f normal[3];
Triangle();
void setVertex(int ind, Vector3f ver);
void setNormal(int ind, Vector3f n);
void setColor(int ind, float r, float g, float b);
Vector3f getColor() const { return color[0]*255; }
void setTexCoord(int ind, float s, float t);
std::array<Vector4f, 3> toVector4() const;
};
定义了一个名为Triangle的类,定义了setVertex()/setNormal()等成员函数,在类外对其进行定义时函数头就应该是 void Triangle::setVertex()这种形式,其返回类型、成员函数名、参数列表都应该与类内声明的形式一致。
例如在代码框架的Triangle.cpp文件中,就有如下:
Triangle::Triangle() {
...
}
void Triangle::setVertex(int ind, Vector3f ver){
v[ind] = ver;
}
struct
struct结构体,默认公有继承,是public的。C++中的struct不同于C中的,C中的struct不能定义函数只能定义数据成员,数据和操作是分开的;C++中的struct可以包含成员函数,可修改public / private / protected,可以直接用{}初始化。
struct test
{
int a;
int b;
};
//完全可以用大括号对数据进行初始化
test A={1,2};
但是,C++有class类了,可以实现struct的功能。
enum class枚举类型
等价于enum struct
旧版本是enum,但有些问题,因此使用enum class/enum struct具有更好的类型安全和类似封装的特性(scoped nature)。参数仅在color作用域内生效。
enum class color{red, green, yellow};
enum class colorx{red, green = 100, yellow};
...
底层数据类型(underlying tyue)
默认为int,可以通过:type:类型 指定任何整形作为底层数据类型。
enum class color: long long {yellow, black};
域
访问需要通过域运算符访问,不可以直接通过访问枚举成员名称来访问,因此名称重复不会造成影响。如下:
#include<iostream>
using namespace std;
enum class color { red, green };
enum class colorx { red=1, green };
int main()
{
//这里用了static_cast<int>强转类型为整型int
cout << static_cast<int>(color::red) << endl;
cout << static_cast<int>(colorx::red) << endl;
return 0;
}
<algorithm>头文件
定义了C++ STL标准中的基础性的算法(均为函数模板)。<algorithm>定义了设计用于元素范围的函数集合。具体有什么函数模板可参考:C++/C++11中头文件algorithm的使用
inline
参考:inline的使用
先来说说什么是栈空间
栈空间也叫栈内存,可以理解成程序放置局部数据的内存空间。栈空间是有限的,频繁大量的使用会导致程序出错,例如某函数的死循环的尽头就是将栈内存耗尽。
为了解决一些频繁调用的小函数大量消耗栈内存的问题,引入了inline修饰符,表示为内联函数。
inline具体如何实现目的
#include<iostream>
using namespace std;
inline string test(int a)
{
return (a % 2 > 0) ? "奇数" : "偶数";
}
int main()
{
int i = 0;
for (i = 1; i < 100; i++)
{
cout << test(i) << endl;
}
return 0;
}
上述例子就是把调用到test(i)的地方都换成了 (a % 2 > 0) ? "奇数" : "偶数",避免频繁调用函数。但是:inline的使用对编译器来说只是一个建议,编译器可以选择忽略这个建议,取决于函数的长短;当你将一个1000多行的函数定位inline,编译器还会按照普通函数进行运行。
类外定义前加上inline关键字
例如本题的rasterize.hpp里:下面的部分就是在类外定义。
...
enum class Buffers
{
Color = 1,
Depth = 2
};
inline Buffers operator|(Buffers a, Buffers b)
{
return Buffers((int)a | (int)b); //位的或运算
}
...
operator
operator是C++的一个关键字,和运算符一起使用(例如:operator+),表示一个运算符重载函数,可将其视为一个函数名。
为什么要用这个
C++提供的运算符只支持基本数据类型和标准库中提供的类操作,对于自己定义的class类,想要实现运算符操作(比大小、判断是否相等)就需要自己定义运算符的实现过程。
在类里实现运算符-成员函数
在类中声明需要重载的运算符,方式跟普通的成员函数一样,名称为operator+运算符,例如:
...
class person
{
private:
int age;
public:
person(int nage)
{
this->age = nage;
}
bool operator==(const person& ps)
{
if(this->age == ps.age)
{
return true;
}
return false;
}
};
...
类外的非类的成员函数-全局函数
代表左操作数的参数必须被显式指定。
...
class person
{
public:
int age;
};
bool operator==(person const& p1 ,person const& p2)
{
if (p1.age == p2.age)
{
return true;
}
else
{
return false;
}
}
...
逻辑运算符&&和||与&和|的区别
(1)与:&不具有短路效果,左边false右边还会执行;&&有短路效果,左边为false右边不执行;
(2)或:|不具有短路效果,左边为true右边还会执行;||有短路效果,左边为true右边不执行。
empalce_front/emplace/emplace_back
是c++11标准引入的三个新成员,这些操作不是拷贝元素,是在容器中构造函数。因此相比push_back等函数能更好地避免内存的拷贝与移动,使容器插入元素的性能得到进一步提升。
c.emplace_back(t) //在c的尾部创建一个值为t的元素
c.emplace_front(t) //在c的头部创建一个值为t的元素
c.emplace(p,t) //在迭代器p所指向的元素之前创建一个值为t的元素,并返回指定新添加元素的迭代器