Skip to content

Commit 08ec4ab

Browse files
authored
Merge pull request #328 from RayTracing/memory-management-2
Memory management 2
2 parents 0afae2e + 70ae85b commit 08ec4ab

31 files changed

+1111
-706
lines changed

books/RayTracingInOneWeekend.html

Lines changed: 118 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -915,20 +915,24 @@
915915
#define HITTABLE_LIST_H
916916

917917
#include "hittable.h"
918+
#include <memory>
918919
#include <vector>
919920

921+
using std::shared_ptr;
922+
using std::make_shared;
923+
920924
class hittable_list: public hittable {
921925
public:
922926
hittable_list() {}
923-
hittable_list(hittable* object) { add(object); }
927+
hittable_list(shared_ptr<hittable> object) { add(object); }
924928

925-
void clear() { objects.clear(); }
926-
void add(hittable* object) { objects.push_back(object); }
929+
void clear() { objects.clear(); }
930+
void add(shared_ptr<hittable> object) { objects.push_back(object); }
927931

928932
virtual bool hit(const ray& r, double tmin, double tmax, hit_record& rec) const;
929933

930934
public:
931-
std::vector<hittable*> objects;
935+
std::vector<shared_ptr<hittable>> objects;
932936
};
933937

934938
bool hittable_list::hit(const ray& r, double t_min, double t_max, hit_record& rec) const {
@@ -952,10 +956,49 @@
952956
[Listing [hittable-list-initial]: <kbd>[hittable_list.h]</kbd> The hittable_list class]
953957
</div>
954958

955-
If you're unfamiliar with C++'s `std::vector`, this is a generic array-like collection of an
956-
arbitrary type. Above, we use a collection of pointers to `hittable`. `std::vector` automatically
957-
grows as more values are added: `objects.push_back(object)` adds a value to the end of the
958-
`std::vector` member variable `objects`.
959+
## Some New C++ Features
960+
961+
The `hittable_list` class code uses two C++ features that may trip you up if you're not normally a
962+
C++ programmer: `vector` and `shared_ptr`.
963+
964+
`shared_ptr<type>` is a pointer to some allocated type, with reference-counting semantics.
965+
Every time you assign its value to another shared pointer (usually with a simple assignment), the
966+
reference count is incremented. As shared pointers go out of scope (like at the end of a block or
967+
function), the reference count is decremented. Once the count goes to zero, the object is deleted.
968+
969+
<div class='together'>
970+
Typically, a shared pointer is first initialized with a newly-allocated object, something like this:
971+
972+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
973+
shared_ptr<thing> thing_ptr = make_shared<thing>(1, true);
974+
auto thing2_ptr = make_shared<thing2>(2, false);
975+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
976+
[Listing [shared-ptr]: An example allocation using `shared_ptr`]
977+
978+
`make_shared<thing>(1, true)` allocates a new instance of type `thing`, using the constructor
979+
arguments `(1, true)`. It returns a `shared_ptr<thing>`. This can be simplified as in the second
980+
line with the `auto` type declaration, because the type is sufficiently defined by the return type
981+
of `make_shared<thing2>`.
982+
</div>
983+
984+
We'll use shared pointers in our code, because it allows multiple objects to use a common object
985+
(for example, a bunch of spheres that all use the same material), and because it makes memory
986+
management automatic and easier to reason about.
987+
988+
`std::shared_ptr` is included with the `<memory>` header.
989+
990+
The second C++ feature you may be unfamiliar with is `std::vector`. This is a generic array-like
991+
collection of an arbitrary type. Above, we use a collection of pointers to `hittable`. `std::vector`
992+
automatically grows as more values are added: `objects.push_back(object)` adds a value to the end of
993+
the `std::vector` member variable `objects`.
994+
995+
`std::vector` is included with the `<vector>` header.
996+
997+
Finally, the `using` statements in listing [hittable-list-initial] tell the compiler that we'll be
998+
getting `shared_ptr` and `make_shared` from the `std` library, so we don't need to prefex these with
999+
`std::` every time we reference them.
1000+
1001+
## Common Constants and Utility Functions
9591002

9601003
<div class='together'></div>
9611004
We need some math constants that we conveniently define in their own header file. For now we only
@@ -968,8 +1011,16 @@
9681011
#ifndef RTWEEKEND_H
9691012
#define RTWEEKEND_H
9701013

971-
#include <limits>
9721014
#include <cmath>
1015+
#include <cstdlib>
1016+
#include <limits>
1017+
#include <memory>
1018+
1019+
1020+
// Usings
1021+
1022+
using std::shared_ptr;
1023+
using std::make_shared;
9731024

9741025
// Constants
9751026

@@ -1040,8 +1091,8 @@
10401091

10411092
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
10421093
hittable_list world;
1043-
world.add(new sphere(vec3(0,0,-1), 0.5));
1044-
world.add(new sphere(vec3(0,-100.5,-1), 100));
1094+
world.add(make_shared<sphere>(vec3(0,0,-1), 0.5));
1095+
world.add(make_shared<sphere>(vec3(0,-100.5,-1), 100));
10451096
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
10461097

10471098
for (int j = ny-1; j >= 0; --j) {
@@ -1218,8 +1269,8 @@
12181269
std::cout << "P3\n" << nx << " " << ny << "\n255\n";
12191270

12201271
hittable_list world;
1221-
world.add(new sphere(vec3(0,0,-1), 0.5));
1222-
world.add(new sphere(vec3(0,-100.5,-1), 100));
1272+
world.add(make_shared<sphere>(vec3(0,0,-1), 0.5));
1273+
world.add(make_shared<sphere>(vec3(0,-100.5,-1), 100));
12231274

12241275
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
12251276
camera cam;
@@ -1650,7 +1701,7 @@
16501701
vec3 p;
16511702
vec3 normal;
16521703
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
1653-
material *mat_ptr;
1704+
shared_ptr<material> mat_ptr;
16541705
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
16551706
double t;
16561707
bool front_face;
@@ -1687,12 +1738,17 @@
16871738
class sphere: public hittable {
16881739
public:
16891740
sphere() {}
1690-
sphere(vec3 cen, double r, material *m) : center(cen), radius(r), mat_ptr(m) {};
1741+
1742+
sphere(vec3 cen, double r, shared_ptr<material> m)
1743+
: center(cen), radius(r), mat_ptr(m) {};
1744+
16911745
virtual bool hit(const ray& r, double tmin, double tmax, hit_record& rec) const;
1746+
1747+
public:
16921748
vec3 center;
16931749
double radius;
16941750
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
1695-
material *mat_ptr;
1751+
shared_ptr<material> mat_ptr;
16961752
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
16971753
};
16981754
bool sphere::hit(const ray& r, double t_min, double t_max, hit_record& rec) const {
@@ -1752,6 +1808,7 @@
17521808
return true;
17531809
}
17541810

1811+
public:
17551812
vec3 albedo;
17561813
};
17571814
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1799,6 +1856,8 @@
17991856
attenuation = albedo;
18001857
return (dot(scattered.direction(), rec.normal) > 0);
18011858
}
1859+
1860+
public:
18021861
vec3 albedo;
18031862
};
18041863
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1847,10 +1906,15 @@
18471906

18481907
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
18491908
hittable_list world;
1850-
world.add(new sphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.7, 0.3, 0.3))));
1851-
world.add(new sphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0))));
1852-
world.add(new sphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2))));
1853-
world.add(new sphere(vec3(-1,0,-1), 0.5, new metal(vec3(0.8, 0.8, 0.8))));
1909+
1910+
world.add(make_shared<sphere>(
1911+
vec3(0,0,-1), 0.5, make_shared<lambertian>(vec3(0.7, 0.3, 0.3))));
1912+
1913+
world.add(make_shared<sphere>(
1914+
vec3(0,-100.5,-1), 100, make_shared<lambertian>(vec3(0.8, 0.8, 0.0))));
1915+
1916+
world.add(make_shared<sphere>(vec3(1,0,-1), 0.5, make_shared<metal>(vec3(0.8, 0.6, 0.2))));
1917+
world.add(make_shared<sphere>(vec3(-1,0,-1), 0.5, make_shared<metal>(vec3(0.8, 0.8, 0.8))));
18541918
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
18551919

18561920
camera cam;
@@ -1912,6 +1976,8 @@
19121976
attenuation = albedo;
19131977
return (dot(scattered.direction(), rec.normal) > 0);
19141978
}
1979+
1980+
public:
19151981
vec3 albedo;
19161982
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
19171983
double fuzz;
@@ -2134,6 +2200,7 @@
21342200
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
21352201
}
21362202

2203+
public:
21372204
double ref_idx;
21382205
};
21392206
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2144,10 +2211,14 @@
21442211
Attenuation is always 1 -- the glass surface absorbs nothing. If we try that out with these parameters:
21452212

21462213
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
2147-
list[0] = new sphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.1, 0.2, 0.5)));
2148-
list[1] = new sphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));
2149-
list[2] = new sphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2), 0.0));
2150-
list[3] = new sphere(vec3(-1,0,-1), 0.5, new dielectric(1.5));
2214+
world.add(make_shared<sphere>(
2215+
vec3(0,0,-1), 0.5, make_shared<lambertian>(vec3(0.1, 0.2, 0.5)));
2216+
2217+
world.add(make_shared<sphere>(
2218+
vec3(0,-100.5,-1), 100, make_shared<lambertian>(vec3(0.8, 0.8, 0.0)));
2219+
2220+
world.add(make_shared<sphere>(vec3(1,0,-1), 0.5, make_shared<metal>(vec3(0.8, 0.6, 0.2), 0.0));
2221+
world.add(make_shared<sphere>(vec3(-1,0,-1), 0.5, make_shared<dielectric>(1.5));
21512222
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21522223
[Listing [scene-dielectric]: <kbd>[main.cc]</kbd> Scene with dielectric sphere]
21532224

@@ -2206,6 +2277,7 @@
22062277
return true;
22072278
}
22082279

2280+
public:
22092281
double ref_idx;
22102282
};
22112283
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2218,11 +2290,12 @@
22182290
to make a hollow glass sphere:
22192291

22202292
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
2221-
list[0] = new sphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.1, 0.2, 0.5)));
2222-
list[1] = new sphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));
2223-
list[2] = new sphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2), 0.3));
2224-
list[3] = new sphere(vec3(-1,0,-1), 0.5, new dielectric(1.5));
2225-
list[4] = new sphere(vec3(-1,0,-1), -0.45, new dielectric(1.5));
2293+
world.add(make_shared<sphere>(vec3(0,0,-1), 0.5, make_shared<lambertian>(vec3(0.1, 0.2, 0.5)));
2294+
world.add(make_shared<sphere>(
2295+
vec3(0,-100.5,-1), 100, make_shared<lambertian>(vec3(0.8, 0.8, 0.0)));
2296+
world.add(make_shared<sphere>(vec3(1,0,-1), 0.5, make_shared<metal>(vec3(0.8, 0.6, 0.2), 0.3));
2297+
world.add(make_shared<sphere>(vec3(-1,0,-1), 0.5, make_shared<dielectric>(1.5));
2298+
world.add(make_shared<sphere>(vec3(-1,0,-1), -0.45, make_shared<dielectric>(1.5));
22262299
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22272300
[Listing [scene-hollow-glass]: <kbd>[main.cc]</kbd> Scene with hollow glass sphere]
22282301
</div>
@@ -2295,8 +2368,8 @@
22952368
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
22962369
auto R = cos(pi/4);
22972370
hittable_list world;
2298-
world.add(new sphere(vec3(-R,0,-1), R, new lambertian(vec3(0, 0, 1))));
2299-
world.add(new sphere(vec3( R,0,-1), R, new lambertian(vec3(1, 0, 0))));
2371+
world.add(make_shared<sphere>(vec3(-R,0,-1), R, make_shared<lambertian>(vec3(0, 0, 1))));
2372+
world.add(make_shared<sphere(>vec3( R,0,-1), R, make_shared<lambertian>(vec3(1, 0, 0))));
23002373
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23012374
[Listing [scene-wide-angle]: <kbd>[main.cc]</kbd> Scene with wide-angle camera]
23022375

@@ -2536,7 +2609,8 @@
25362609
hittable_list random_scene() {
25372610
hittable_list objects;
25382611

2539-
objects.add(new sphere(vec3(0,-1000,0), 1000, new lambertian(vec3(0.5, 0.5, 0.5))));
2612+
objects.add(make_shared<sphere>(
2613+
vec3(0,-1000,0), 1000, make_shared<lambertian>(vec3(0.5, 0.5, 0.5))));
25402614

25412615
int i = 1;
25422616
for (int a = -11; a < 11; a++) {
@@ -2547,23 +2621,29 @@
25472621
if (choose_mat < 0.8) {
25482622
// diffuse
25492623
auto albedo = vec3::random() * vec3::random();
2550-
objects.add(new sphere(center, 0.2, new lambertian(albedo)));
2624+
objects.add(
2625+
make_shared<sphere>(center, 0.2, make_shared<lambertian>(albedo)));
25512626
} else if (choose_mat < 0.95) {
25522627
// metal
25532628
auto albedo = vec3::random(.5, 1);
25542629
auto fuzz = random_double(0, .5);
2555-
objects.add(new sphere(center, 0.2, new metal(albedo, fuzz)));
2630+
objects.add(
2631+
make_shared<sphere>(center, 0.2, make_shared<metal>(albedo, fuzz)));
25562632
} else {
25572633
// glass
2558-
objects.add(new sphere(center, 0.2, new dielectric(1.5)));
2634+
objects.add(make_shared<sphere>(center, 0.2, make_shared<dielectric>(1.5)));
25592635
}
25602636
}
25612637
}
25622638
}
25632639

2564-
objects.add(new sphere(vec3(0, 1, 0), 1.0, new dielectric(1.5)));
2565-
objects.add(new sphere(vec3(-4, 1, 0), 1.0, new lambertian(vec3(0.4, 0.2, 0.1))));
2566-
objects.add(new sphere(vec3(4, 1, 0), 1.0, new metal(vec3(0.7, 0.6, 0.5), 0.0)));
2640+
objects.add(make_shared<sphere>(vec3(0, 1, 0), 1.0, make_shared<dielectric>(1.5)));
2641+
2642+
objects.add(
2643+
make_shared<sphere>(vec3(-4, 1, 0), 1.0, make_shared<lambertian>(vec3(0.4, 0.2, 0.1))));
2644+
2645+
objects.add(
2646+
make_shared<sphere>(vec3(4, 1, 0), 1.0, make_shared<metal>(vec3(0.7, 0.6, 0.5), 0.0)));
25672647

25682648
return objects;
25692649
}

0 commit comments

Comments
 (0)