Some New C++ Features 1
The hittable_list
class code uses some C++ features that may trip you up if you're not normally a C++ programmer: vector
, shared_ptr
, and make_shared
. 2
shared_ptr<type>
is a pointer to some allocated type, with reference-counting semantics. Every time you assign its value to another shared pointer (usually with a simple assignment), the reference count is incremented. As shared pointers go out of scope (like at the end of a block or function), the reference count is decremented. Once the count goes to zero, the object is safely deleted. 3
Typically, a shared pointer is first initialized with a newly-allocated object, something like this:
#![allow(unused)] fn main() { let double_ptr: Rc<double> = Rc::new(0.37); let vec3_ptr: Rc<Vec3> = Rc::new(Vec3::new(1.414214, 2.718281, 1.618034)); let sphere_ptr: Rc<Sphere> = Rc::new(Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0)); }
Listing 22: An example allocation using shared_ptr
make_shared<thing>(thing_constructor_params ...)
allocates a new instance of type thing, using the constructor parameters. It returns a shared_ptr<thing>
. 4
Since the type can be automatically deduced by the return type of make_shared<type>(...)
, the above lines can be more simply expressed using C++'s auto
type specifier: 5
#![allow(unused)] fn main() { let double_ptr = Rc::new(0.37); let vec3_ptr = Rc::new(Vec3::new(1.414214, 2.718281, 1.618034)); let sphere_ptr = Rc::new(Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0)); }
Listing 23: An example allocation using shared_ptr with auto type
We'll use shared pointers in our code, because it allows multiple geometries to share a common instance (for example, a bunch of spheres that all use the same color material), and because it makes memory management automatic and easier to reason about.
std::shared_ptr
is included with the <memory>
header.6
The second C++ feature you may be unfamiliar with is std::vector
. This is a generic array-like collection of an arbitrary type. Above, we use a collection of pointers to hittable
. std::vector
automatically grows as more values are added: objects.push_back(object)
adds a value to the end of the std::vector
member variable objects
.
std::vector
is included with the ``
Finally, the using
statements in listing 21 tell the compiler that we'll be getting shared_ptr
and make_shared
from the std
library, so we don't need to prefix these with std::
every time we reference them.
-
This chapter can be safely skipped when the code of last chapter is clear. ↩
-
The Rust equivalents are
Vec
,Rc
and a simplenew
method of the reference counting smart pointer. ↩ -
Here we use
Rc
. In contrast to the C++shared_ptr
, the contained value of anRc
is inmutable. Enclosing the value with a cell based type like for exampleRefCell
would allow for interior mutability. However in this case, all objects are created on startup and do not change over the course of the program lifetime which is why a simpleRc
is sufficient. ↩ -
Rust has no constructors, instead it is convention to fill structs with the
new
method, the implementation of traits likeDefault
,From
or similar, or to use any method that returnsSelf
. So technically something likemake_shared
does not exist in Rust. ↩ -
The type annotations in the last listing were not nessecary, Rust's
let
declarations can in this case infere the types. ↩ -
Rc
is found instd::rc::Rc
. ↩ -
Vec
is included in Rust's std prelude and does have to be included to be used. ↩