Simple dataset class in C++ As part of my training, I implemented a dataset class in C++ that allows me to create synthetic datasets to get more familiar with features that C++ offers such as object oriented programming.
This implementation uses a simple method to create two intertwined spirals as shown in the following image.

The output looks as follows:
x y label
-3.85616 -0.987058 0
1.16 4.6764 0
2.66279 2.13692 1
-0.643551 3.81989 0
-0.252046 -0.888411 0

Please be as hard as possible with this implementation and give me constructive feedback.
I would appreciate advice especially in the following areas:

Code style (readability, naming conventions, etc...)
Class design
Efficiency (how to avoid unnecessary complexity)
Reinventing the wheel (does the STL offer functionality that I should use?)

main.cpp
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &quot;data.hpp&quot;

int main() {
    unsigned int n_points = 3000;
    Data data (n_points);
    std::vector&lt;Point&gt; d;
    d = data.get();

    for (std::vector&lt;Point&gt;::iterator it = d.begin(); it != d.end(); ++it) {
        std::cout &lt;&lt; it-&gt;x &lt;&lt; &quot; &quot; &lt;&lt; it-&gt;y &lt;&lt; &quot; &quot; &lt;&lt; it-&gt;label &lt;&lt; std::endl;
    }
    return 0;
}

data.hpp
#ifndef DATA_H
#define DATA_H

#include &lt;vector&gt;
#include &lt;cmath&gt;
#include &quot;util.hpp&quot;

class Data {
    public:
        Data (unsigned int);
        unsigned int n_samples;
        unsigned int n_dims = 2;
        unsigned int n_classes = 2;
        std::vector&lt;Point&gt; get();

    private:
        Random random;
        std::vector&lt;Point&gt; data;
        std::vector&lt;Point&gt; generate_data();
};

Data::Data (unsigned int _n_samples) {
    n_samples = _n_samples;
}

std::vector&lt;Point&gt; Data::generate_data() {
    std::vector&lt;Point&gt; data(n_samples);
    std::vector&lt;Point&gt;::iterator it;

    double noise = 1.0;
    double spread = 4.0;
    for (it = data.begin(); it != data.end(); ++it) {
        double r = 3.14 * spread * random.rand();
        double r1 = -cos(r) * r + noise * random.rand();
        double r2 = sin(r) * r + noise * random.rand();
        if (random.rand() &lt; 0.5) {
            it-&gt;x = r1;
            it-&gt;y = r2;
            it-&gt;label = 1;
        } else {
            it-&gt;x = -r1;
            it-&gt;y = -r2;
            it-&gt;label = 0;
        } 
    }

    return data;
}

std::vector&lt;Point&gt; Data::get() {
    std::vector&lt;Point&gt; data(n_samples);
    std::vector&lt;Point&gt;::iterator it;
    data = generate_data();
    return data;
}

#endif /* DATA_H */

util.hpp
#ifndef UTIL_H
#define UTIL_H

#include &lt;cstdlib&gt;
#include &lt;ctime&gt;

struct Point {
    double x;
    double y;
    unsigned int label;
};

class Random {
    public:
        Random ();
        double rand();
};

Random::Random () {
    std::srand(static_cast&lt;unsigned int&gt;(std::time(nullptr)));
}

double Random::rand() {
    return static_cast&lt;double&gt; (std::rand()) / static_cast&lt;double&gt; (RAND_MAX + 1U);
}

#endif /* UTIL_H */
Here are some things that may help you improve your program.
Separate interface from implementation
The interface goes into a header file and the implementation (that is, everything that actually emits bytes including all functions and data) should be in a separate .cpp file.  The reason is that you might have multiple source files including the .h file but only one instance of the corresponding .cpp file.  In other words, split your existing data.hpp and util.hpp files into a .h file and a .cpp file.
Understand the use of namespaces
Your code is correctly using &lt;cmath&gt; instead of &lt;math.h&gt;.  The difference between the two forms is that the former defines things within the std:: namespace versus into the global namespace.  So this implies that instead of using cos and sin, the code should be using std::cos and std::sin
Use const where practical
There are a number of named constants in the program such as unsigned int n_dims = 2;.  It's very good practice to name these, but even better is to declare them const or better still constexpr.  The reason is that it allows the compiler to perform better optimizations without sacrificing readability.
Don't use std::endl if you don't really need it
The difference betweeen std::endl and '\n' is that '\n' just emits a newline character, while std::endl actually flushes the stream.  This can be time-consuming in a program with a lot of I/O and is rarely actually needed.  It's best to only use std::endl when you have some good reason to flush the stream and it's not very often needed for simple programs such as this one.  Avoiding the habit of using std::endl when '\n' will do will pay dividends in the future as you write more complex programs with more I/O and where performance needs to be maximized.
Use &quot;range for&quot; and simplify your code
The code currently includes these lines:
for (std::vector&lt;Point&gt;::iterator it = d.begin(); it != d.end(); ++it) {
    std::cout &lt;&lt; it-&gt;x &lt;&lt; &quot; &quot; &lt;&lt; it-&gt;y &lt;&lt; &quot; &quot; &lt;&lt; it-&gt;label &lt;&lt; std::endl;
}

Since C++11, however, we have a range for which simplifies it to this:
for (const auto&amp; point : d) {
    std::cout &lt;&lt; point.x &lt;&lt; &quot; &quot; &lt;&lt; point.y &lt;&lt; &quot; &quot; &lt;&lt; point.label &lt;&lt; '\n';
}

Write a stream inserter for your class
We can further refine the for loop above by creating a custom inserter for the Point class:
std::ostream&amp; operator&lt;&lt;(std::ostream&amp; out, const Point&amp; point) {
    return out &lt;&lt; point.x &lt;&lt; &quot; &quot; &lt;&lt; point.y &lt;&lt; &quot; &quot; &lt;&lt; point.label;
}

Now our loop in main is even simpler:
for (const auto&amp; point : d) {
    std::cout &lt;&lt; point &lt;&lt; '\n';
}

Use standard library algorithms
We can use std::copy to copy from the vector to std::cout, replacing the loop with a single statement:
std::copy(d.begin(), d.end(), std::ostream_iterator&lt;Point&gt;(std::cout, &quot;\n&quot;));

Consider using a better random number generator
If you are using a compiler that supports at least C++11, consider using a better random number generator.  In particular, instead of rand, you might want to look at std::uniform_real_distribution and friends in the &lt;random&gt; header.  Also, instead of doing this:
if (random.rand() &lt; 0.5) {
    it-&gt;x = r1;
    it-&gt;y = r2;
    it-&gt;label = 1;
} else {
    it-&gt;x = -r1;
    it-&gt;y = -r2;
    it-&gt;label = 0;
}

I would suggest using a std::bernoulli_distribution.
Rethink the classes
Rather than having the vaguely named Data class creating Points, I'd suggest moving that functionality into the Point class.  Since this is actually creating a Point, it makes sense to me to create a custom constructor with this functionality:
Point::Point() {
    static std::random_device rd;
    static std::mt19937 gen(rd());
    static std::uniform_real_distribution&lt;&gt; spread(0.0, 4.0*3.14);
    static std::uniform_real_distribution&lt;&gt; noise(0.0, 1.0);
    static std::bernoulli_distribution type(0.5);
    static constexpr double multiplier[]{-1.0, +1.0};

    label = type(gen);
    double r = spread(gen);
    x = multiplier[label] * (-std::cos(r) * r + noise(gen));
    y = multiplier[label] * (+std::sin(r) * r + noise(gen));
}

Minimize memory use
It's not critical for this program, but rather than storing everything in a vector only to print it and then destroy the vector, a more memory efficient approach would be to generate each point as needed and emit it directly.  Using the Point constructor and stream inserter as defined above, this becomes trivial.
Result
Here's the entire program as rewritten with all of these ideas:
#include &lt;random&gt;
#include &lt;iostream&gt;
#include &lt;iterator&gt;
#include &lt;cmath&gt;

struct Point {
    Point();
    double x;
    double y;
    unsigned int label;
};

Point::Point() {
    static std::random_device rd;
    static std::mt19937 gen(rd());
    static std::uniform_real_distribution&lt;&gt; spread(0.0, 4.0*3.14);
    static std::uniform_real_distribution&lt;&gt; noise(0.0, 1.0);
    static std::bernoulli_distribution type(0.5);
    static constexpr double multiplier[]{-1.0, +1.0};

    label = type(gen);
    double r = spread(gen);
    x = multiplier[label] * (-std::cos(r) * r + noise(gen));
    y = multiplier[label] * (+std::sin(r) * r + noise(gen));
}

std::ostream&amp; operator&lt;&lt;(std::ostream&amp; out, const Point&amp; point) {
    return out &lt;&lt; point.x &lt;&lt; &quot; &quot; &lt;&lt; point.y &lt;&lt; &quot; &quot; &lt;&lt; point.label;
}

int main() {
    for (auto i{3000}; i; --i) {
        std::cout &lt;&lt; Point() &lt;&lt; '\n';
    }
}