I have class XOBoard that present an array that is size n*n,each cell of the array is an Object called Cell.
Each Cell object is defined by
class Cell {
private:
char ch;
public:
Cell(char ch = '.');
char getCellValue();
void setCellValue(char nch);
};
Board is defined this way:
class XOBoard {
private:
int n;
Cell **Board;
};
XOBoard::XOBoard(int n) { //constructor
this->n = (n >= 3) ? n : 3;
Board = new Cell*[n];
for (int i = 0; i < n; i++) {
Board[i] = new Cell[n];
}
}
I wanted to get to a specific Cell value by using this method: board1[{1,2}], but I want to check if the values that sent to me is withing the range(n), but unfortantly I was unable to get to the Board array, and to n variable.
Here is the code:
XOBoard& operator[](list<int> list){
int x = list.front(), y = list.back();
return Board[x][y].getCellValue();
}
Thanks a head!
As mentioned in the comments, using operator[] for multidimensional subscripting is unconventional, but if you want that, you should make sure you get the correct amount of values (2 in this case) and that you return the correct type (a Cell& in this case).
Also be aware of shadowing. If you try to construct a Board with a value less than 3, you'll set this->n to 3 but go on with the construction using the erroneous n (that may even be a negative value).
More comments inline:
#include <iostream>
#include <stdexcept>
#include <tuple>
class Cell {
private:
char ch;
public:
Cell(char nch = '.') : // after the colon comes the member initializer list
ch(nch) // which is usually good to use
{
// if(ch is not valid) throw ...
}
char getCellValue() const { return ch; }
// a convenient conversion operator to automatically
// convert a Cell to a char where a char is needed
// (like when streaming a Cell to std::cout)
operator char() const { return ch; }
// void setCellValue(char nch); // replaced by operator=
Cell& operator=(char nch) {
// if(nch is not valid) throw ...
ch = nch;
return *this;
}
};
class XOBoard {
private:
size_t n; // use an unsigned type for sizes/indices
Cell** Board;
public:
// constructor
XOBoard(size_t xy_size) : // a member initializer list again
n(xy_size >= 3 ? xy_size : 3), // assign to "n" here
Board(new Cell*[n]) // the correct n is now used
{
// if the below construction fails, a bad_alloc will be thrown.
// you need to add code to clean up what you've already allocated to take
// care of that situation.
for(size_t i = 0; i < n; i++) {
Board[i] = new Cell[n];
}
}
// Copying or moving need careful handling of the pointers.
// Read "The rule of three/five/zero". Until then, disable it.
XOBoard(const XOBoard&) = delete;
XOBoard& operator=(const XOBoard&) = delete;
// destructor
~XOBoard() {
for(size_t i = 0; i < n; i++) delete[] Board[i];
delete[] Board;
}
// added for convenience
size_t size() const { return n; }
// return a Cell& and use a std::pair since you
// expect exactly 2 values
Cell& operator[](std::pair<size_t, size_t> pos) {
auto& [x, y] = pos;
if(x>=n || y>=n)
throw std::out_of_range("{"+std::to_string(x)+","+std::to_string(y)+"}");
return Board[x][y];
}
};
int main() {
try {
XOBoard a{2}; // trying an invalid size
std::cout << a.size() << '\n';
a[{2, 2}] = 'a';
std::cout << a[{2, 2}] << '\n';
Cell x = 'b';
a[{2, 2}] = x;
std::cout << a[{2, 2}] << '\n';
a[{2, 3}] = 'c'; // index out of bounds
} catch(const std::out_of_range& ex) {
std::cerr << "out_of_range exception: " << ex.what() << '\n';
}
}
Output:
3
a
b
out_of_range exception: {2,3}
You should try to avoid raw pointers and actual multidimensional arrays. It's often better to emulate dimensionality by allocating a 1d array and provide an interface to the user that calculates the correct element to work on.
First, I'm not good at english and also first time in StackOverflow, but I try to explain about my code's problem.
I was asked to make my own Vector(similar thing) from my professer, and there's a problem in fuction which returns a reference to the element at the requested position in the vector container. If the requested position is out of range, it should output some messages and terminate the program.
I should make this to Operator overloading, and this is my code.
double operator [](int n, const MyDoubleVector& _mV)//The arror come out at this line.
{
if(n > num)//'num' is private reference in class to count array. it typed int.
{
return 0;
}
return &_mV.data[n];//'data' is private reference in class. It declare like 'double *data = new double [num];'
}
I saw that sometimes 'friend' solve this, but when I put 'friend' in this line, it said me like "operator[] must be a member function."
Finally, Ihave no idea how to do. Would you please help me?
You need to implement the overload of operator[] as a member function of your class MyDoubleVector.
Here's the definition :
double & MyDoubleVector::operator[](int index);
operator [] must be defined as a member of the class.
example:
#include <iostream>
#include <cstdlib>
#include <algorithm>
struct MyDoubleVector
{
MyDoubleVector()
{}
MyDoubleVector(MyDoubleVector const& other)
{
// very naiive copy constructor
if (other.data)
{
std::for_each(other.data, other.data + other.num, [&](double val)
{
this->push(val);
});
}
}
MyDoubleVector& operator=(MyDoubleVector const& other)
{
auto temp = other; // invoke copy constructor
std::swap(num, temp.num);
std::swap(capacity, temp.capacity);
std::swap(data, temp.data);
return *this;
}
~MyDoubleVector()
{
delete [] data;
}
double& operator [](int n);
/** either define the method inline like this...
{
if(n > num)
{
std::cerr << "MyDoubleVector::operator[]: index " << n << " out of range" << std::endl;
std::exit(100);
}
return data[n];
}
**/
void push(double val)
{
if (num == capacity)
{
more();
}
data[num++] = val;
}
private:
void more()
{
if (!data)
{
data = new double [10];
capacity = 16;
}
else
{
auto newcapacity = capacity * 2;
auto newdata = new double [newcapacity];
std::copy(data, data + capacity, newdata);
std::swap(data, newdata);
capacity = newcapacity;
delete [] newdata;
}
}
int num = 0;
int capacity = 0;
double* data = nullptr;
};
/** ...
** or out of line like this
**/
double& MyDoubleVector::operator [](int n)
{
if(n > num)
{
std::cerr << "MyDoubleVector::operator[]: index " << n << " out of range" << std::endl;
std::exit(100);
}
return data[n];
}
int main()
{
MyDoubleVector v;
v.push(10.0);
v[1];
}
I find myself writing a lot of functions that begin with many preconditions, and then I have to figure out how to handle all the invalid inputs and write tests for them.
Note that the codebase I work in does not allow throwing exceptions, in case that becomes relevant in this question.
I am wondering if there is any C++ design pattern where instead of having preconditions, input arguments are passed via wrapper classes that guarantee invariants. For example suppose I want a function to return the max value in a vector of ints. Normally I would do something like this:
// Return value indicates failure.
int MaxValue(const std::vector<int>& vec, int* max_value) {
if (vec.empty()) {
return EXIT_FAILURE;
}
*max_value = vec[0];
for (int element : vec) {
if (element > *max_value) {
*max_value = element;
}
}
return EXIT_SUCCESS;
}
But I am wondering if there is a design pattern to do something like this:
template <class T>
class NonEmptyVectorWrapper {
public:
static std::unique_ptr<NonEmptyVectorWrapper>
Create(const std::vector<T>& non_empty_vector) {
if (non_empty_vector.empty()) {
return std::unique_ptr<NonEmptyVectorWrapper>(nullptr);
}
return std::unique_ptr<NonEmptyVectorWrapper>(
new NonEmptyVectorWrapper(non_empty_vector));
}
const std::vector<T>& vector() const {
return non_empty_vector_;
}
private:
// Could implement move constructor/factory for efficiency.
NonEmptyVectorWrapper(const std::vector<T>& non_empty_vector)
: non_empty_vector_(non_empty_vector) {}
const std::vector<T> non_empty_vector_;
};
int MaxValue(const NonEmptyVectorWrapper<int>& vec_wrapper) {
const std::vector<int>& non_empty_vec = vec_wrapper.vector();
int max_value = non_empty_vec[0];
for (int element : non_empty_vec) {
if (element > max_value) {
max_value = element;
}
}
return max_value;
}
The main pro here is that you avoid unnecessary error handling in the function. A more complicated example where this could be useful:
// Finds the value in maybe_empty_vec which is closest to integer n.
// Return value indicates failure.
int GetValueClosestToInt(
const std::vector<int>& maybe_empty_vec,
int n,
int* closest_val);
std::vector<int> vector = GetRandomNonEmptyVector();
for (int i = 0; i < 10000; i++) {
int closest_val;
int success = GetValueClosestToInt(vector, i, &closest_val);
if (success) {
std::cout << closest_val;
} else {
// This never happens but we should handle it.
}
}
which wastefully checks that the vector is non-empty each time and checks for failure, versus
// Returns the value in the wrapped vector closest to n.
int GetValueClosestToInt(
const NonEmptyVectorWrapper& non_empty_vector_wrapper,
int n);
std::unique_ptr<NonEmptyVectorWrapper> non_empty_vector_wrapper =
NonEmptyVectorWrapper::Create(GetRandomNonEmptyVector());
for (int i = 0; i < 10000; i++) {
std::cout << GetValueClosestToInt(*non_empty_vector_wrapper, i);
}
which can't fail and gets rid of the needless input checking.
Is this design pattern a good idea, is there a better way to do it, and is there a name for it?
Here is my code:
class obj140{
public:
int x;
explicit obj140(int y):x(y){ }
bool operator<(const obj140& rhs) const{
return x < rhs.x;
}
};
int main() {
obj140 * wtf = new obj140[5] {obj140(1),obj140(1),obj140(3),obj140(4),obj140(5)};
std::set<obj140> orm(wtf,wtf+5);
}
Is this possible? like copying pointers to a set? I have no errors but I have no idea on how to access it though.
How do i print out the values from orm set?
I modified your code slightly to make what's going on easier to see and as an example of one way to get a look at the items stored in the set.
class obj140
{
public:
int x;
explicit obj140(int y) :x(y)
{
}
bool operator<(const obj140& rhs) const
{
return x < rhs.x;
}
void print() const
{
std::cout << x << std::endl;
}
};
int main()
{
obj140 * wtf = new obj140[5]
{ obj140(1), obj140(1), obj140(3), obj140(4), obj140(5) };
std::set<obj140> orm(wtf, wtf + 5);
for (auto it = orm.begin(); it != orm.end(); ++it)
{
it->print();
}
delete[] wtf; //edit. Forgot to clean up the pointer.
return 0;
}
Output:
1
3
4
5
What you are doing works and loaded the set. Since sets only store unique values (and order them, which makes for a great quickie sort if you need one) the second add of obj140(1) got discarded.
I want to create a function that returns a contiguous 2D array in C++.
It is not a problem to create the array using the command:
int (*v)[cols] = new (int[rows][cols]);
However, I am not sure how to return this array as a general type for a function. The function is:
NOT_SURE_WHAT_TYPE create_array(int rows, int cols)
{
int (*v)[cols] = new (int[rows][cols]);
return v;
}
I tried double*[] and double** and both don't work. I wouldn't want to use double*, since I want to access this array from outside as a 2D array.
Related question: How do I declare a 2d array in C++ using new?
If you want to create an array where the data is contiguous and you don't want a 1-dimensional array (i.e. you want to use the [][] syntax), then the following should work. It creates an array of pointers, and each pointer points to a position into a pool of memory.
#include <iostream>
#include <exception>
template <typename T>
T** create2DArray(unsigned nrows, unsigned ncols, const T& val = T())
{
if (nrows == 0)
throw std::invalid_argument("number of rows is 0");
if (ncols == 0)
throw std::invalid_argument("number of columns is 0");
T** ptr = nullptr;
T* pool = nullptr;
try
{
ptr = new T*[nrows]; // allocate pointers (can throw here)
pool = new T[nrows*ncols]{val}; // allocate pool (can throw here)
// now point the row pointers to the appropriate positions in
// the memory pool
for (unsigned i = 0; i < nrows; ++i, pool += ncols )
ptr[i] = pool;
// Done.
return ptr;
}
catch (std::bad_alloc& ex)
{
delete [] ptr; // either this is nullptr or it was allocated
throw ex; // memory allocation error
}
}
template <typename T>
void delete2DArray(T** arr)
{
delete [] arr[0]; // remove the pool
delete [] arr; // remove the pointers
}
int main()
{
try
{
double **dPtr = create2DArray<double>(10,10);
dPtr[0][0] = 10; // for example
delete2DArray(dPtr); // free the memory
}
catch(std::bad_alloc& ex)
{
std::cout << "Could not allocate array";
}
}
Note that only 2 allocations are done. Not only is this more efficient due to the lesser amounts of allocations done, we now have a better chance of doing a rollback of the allocated memory if a memory allocation fails, unlike the "traditional" way of allocating a 2D array in non-contiguous memory:
// The "traditional" non-contiguous allocation of a 2D array (assume N x M)
T** ptr;
ptr = new T*[N];
for (int i = 0; i < N; ++i)
ptr[i] = new T [M]; // <<-- What happens if new[] throws at some iteration?
If new[] throws an exception somewhere during the operation of the for loop, you have to roll back all of the successful calls to new[] that happened previously -- that requires more code and adds complexity.
Note how you deallocate the memory in the contiguous version -- just two calls to delete[] when allocated contiguously instead of a loop calling delete[] for each row.
Also, since the data is in contiguous memory, algorithms, functions, etc. that assume that the data is in contiguous memory, just like a one-dimensional array, can now be used by specifying the start and end range for the M*N matrix:
[&array[0][0], &array[M-1][N])
For example:
std::sort(&myArray[0][0], &myArray[M-1][N]);
will sort the entire matrix in ascending order, starting from index [0][0] up until the last index [M-1][N-1].
You can improve on the design by making this a true class instead of having allocation / deallocation as 2 separate functions.
Edit: The class is not RAII-like, just as the comment says. I leave that as an exercise for the reader. One thing missing from the code above is the check that nRows and nCols are > 0 when creating such an array.
Edit 2: Added a try-catch to ensure a proper roll back of the memory allocation is done if a std::bad_alloc exception is thrown attempting to allocate memory.
Edit: For a 3 dimensional array example of code similar to the above see this answer. Included is code to roll back allocations if the allocation fails.
Edit: Rudimentary RAII class added:
template <typename T>
class Array2D
{
T** data_ptr;
unsigned m_rows;
unsigned m_cols;
T** create2DArray(unsigned nrows, unsigned ncols, const T& val = T())
{
T** ptr = nullptr;
T* pool = nullptr;
try
{
ptr = new T*[nrows]; // allocate pointers (can throw here)
pool = new T[nrows*ncols]{ val }; // allocate pool (can throw here)
// now point the row pointers to the appropriate positions in
// the memory pool
for (unsigned i = 0; i < nrows; ++i, pool += ncols)
ptr[i] = pool;
// Done.
return ptr;
}
catch (std::bad_alloc& ex)
{
delete[] ptr; // either this is nullptr or it was allocated
throw ex; // memory allocation error
}
}
public:
typedef T value_type;
T** data() {
return data_ptr;
}
unsigned get_rows() const {
return m_rows;
}
unsigned get_cols() const {
return m_cols;
}
Array2D() : data_ptr(nullptr), m_rows(0), m_cols(0) {}
Array2D(unsigned rows, unsigned cols, const T& val = T())
{
if (rows == 0)
throw std::invalid_argument("number of rows is 0");
if (cols == 0)
throw std::invalid_argument("number of columns is 0");
data_ptr = create2DArray(rows, cols, val);
m_rows = rows;
m_cols = cols;
}
~Array2D()
{
if (data_ptr)
{
delete[] data_ptr[0]; // remove the pool
delete[] data_ptr; // remove the pointers
}
}
Array2D(const Array2D& rhs) : m_rows(rhs.m_rows), m_cols(rhs.m_cols)
{
data_ptr = create2DArray(m_rows, m_cols);
std::copy(&rhs.data_ptr[0][0], &rhs.data_ptr[m_rows-1][m_cols], &data_ptr[0][0]);
}
Array2D(Array2D&& rhs) noexcept
{
data_ptr = rhs.data_ptr;
m_rows = rhs.m_rows;
m_cols = rhs.m_cols;
rhs.data_ptr = nullptr;
}
Array2D& operator=(Array2D&& rhs) noexcept
{
if (&rhs != this)
{
swap(rhs, *this);
rhs.data_ptr = nullptr;
}
return *this;
}
void swap(Array2D& left, Array2D& right)
{
std::swap(left.data_ptr, right.data_ptr);
std::swap(left.m_cols, right.m_cols);
std::swap(left.m_rows, right.m_rows);
}
Array2D& operator = (const Array2D& rhs)
{
if (&rhs != this)
{
Array2D temp(rhs);
swap(*this, temp);
}
return *this;
}
T* operator[](unsigned row)
{
return data_ptr[row];
}
const T* operator[](unsigned row) const
{
return data_ptr[row];
}
void create(unsigned rows, unsigned cols, const T& val = T())
{
*this = Array2D(rows, cols, val);
}
};
int main()
{
try
{
Array2D<double> dPtr(10, 10);
std::cout << dPtr[0][0] << " " << dPtr[1][1] << "\n";
}
catch (std::exception& ex)
{
std::cout << ex.what();
}
}
Unless the size of the two dimensions is known at compile time, your don't have much choice: allocate a single rows*cols array of ints, and roll your own 2D indexing with integer multiplication and addition. Wrapping this in a class can produce a nice-looking syntax for accessing array elements with square bracket operator. Since your array is 2D, you will need to use proxy (AKA "surrogate") objects for the first level of data access.
Here is a small sample code that uses std::vector<T> for maintaining a contiguous memory region in dynamic memory:
template<class T>
class Array2D {
vector<T> data;
size_t cols;
public:
// This is the surrogate object for the second-level indexing
template <class U>
class Array2DIndexer {
size_t offset;
vector<U> &data;
public:
Array2DIndexer(size_t o, vector<U> &dt) : offset(o), data(dt) {}
// Second-level indexing is done in this function
T& operator[](size_t index) {
return data[offset+index];
}
};
Array2D(size_t r, size_t c) : data (r*c), cols(c) {}
// First-level indexing is done in this function.
Array2DIndexer<T> operator[](size_t index) {
return Array2DIndexer<T>(index*cols, data);
}
};
You can now use Array2D<int> as if it were a built-in C++ array:
Array2D<int> a2d(10, 20);
for (int r = 0 ; r != 10 ; r++) {
for (int c = 0 ; c != 20 ; c++) {
a2d[r][c] = r+2*c+1;
}
}
Running demo on ideone.
Since you're using C++ and not C, I would recommend to use one vector instead of messing around with new/delete.
You can define one contiguous block of memory like this:
std::vector<int> my_matrix(rows*cols);
And now you access this vector in a 2d-array-like way with the formula i*n + j, with i being the row index, j the column index and n the length of a row:
my_matrix[i*n + j];
That's the same as accessing a 2d array with array[i][j]. But now you have the advantage of one contiguous block of memory, you don't need to bother about new/delete and you can easily share and return this vector object with functions.
handling raw memory ressources is often icky. Best shot is a simple wrapper as :
struct array2D : private std::vector<int>
{
typedef std::vector<int> base_type;
array2D() : base_type(), height_(0), width_(0) {}
array2D(std::size_t h, std::size_t w) : base_type(h*w), height_(h), width_(w);
int operator()(std::size_t i, std::size_t j) const
{
return base_type::operator[](i+j*height_);
}
int& operator()(std::size_t i, std::size_t j)
{
return base_type::operator[](i+j*height_);
}
std::size_t rows() const { return height_; }
std::size_t cols() const { return width_; }
private:
std::size_t height_, width_;
}
private inheritance let you grab all the goodies from vector, just add your 2D constructor. Ressources management is free as vector ctor/dtor will do their magic. Obviously, the i+h*j can be changed to whateever storage order you want.
vector< vector< int > > is 2D but won't be contiguous in memory.
Your function then become :
array2D create_array(int rows, int cols)
{
return array2D(cols,rows);
}
EDIT:
You can also retrieve other vector interface parts like begin/end or size with the usign clause to make the private inherited member functions public again.
None of the ways of defining a 2D dynamic array in standard C++ are entirely satisfactory in my opinion.
You end up having to roll your own solutions. Luckily there is already a solution in Boost. boost::multi_array:
#include "boost/multi_array.hpp"
template<typename T>
boost::multi_array<T, 2> create_array(int rows, int cols) {
auto dims = boost::extents[rows][cols];
return boost::multi_array<T, 2>(dims);
}
int main() {
auto array = create_array<int>(4, 3);
array[3][2] = 0;
}
Live demo.
The "Rudimentary RAll" class provided by PaulMcKenzie is an excellent solution. In my use of it I did find a memory leak which is fixed in the version shown below.
The memory leak was due to an issue with
Array2D& operator=(Array2D&& rhs) noexcept.
The statement rhs.m_dataPtr = nullPtr needed to be removed in order to allow the rhs destructor to delete the original data (pool and pointers) swapped from lhs.
Here is the corrected code for the "Rudimentary RAll" class provided by PaulMcKenzie
template <typename T>
class Array2D
{
T** data_ptr;
unsigned m_rows;
unsigned m_cols;
T** create2DArray(unsigned nrows, unsigned ncols, const T& val = T())
{
T** ptr = nullptr;
T* pool = nullptr;
try
{
ptr = new T*[nrows]; // allocate pointers (can throw here)
pool = new T[nrows*ncols]{ val }; // allocate pool (can throw here)
// now point the row pointers to the appropriate positions in
// the memory pool
for (unsigned i = 0; i < nrows; ++i, pool += ncols)
ptr[i] = pool;
// Done.
return ptr;
}
catch (std::bad_alloc& ex)
{
delete[] ptr; // either this is nullptr or it was allocated
throw ex; // memory allocation error
}
}
public:
typedef T value_type;
T** data() {
return data_ptr;
}
unsigned get_rows() const {
return m_rows;
}
unsigned get_cols() const {
return m_cols;
}
Array2D() : data_ptr(nullptr), m_rows(0), m_cols(0) {}
Array2D(unsigned rows, unsigned cols, const T& val = T())
{
if (rows == 0)
throw std::invalid_argument("number of rows is 0");
if (cols == 0)
throw std::invalid_argument("number of columns is 0");
data_ptr = create2DArray(rows, cols, val);
m_rows = rows;
m_cols = cols;
}
~Array2D()
{
if (data_ptr)
{
delete[] data_ptr[0]; // remove the pool
delete[] data_ptr; // remove the pointers
}
}
Array2D(const Array2D& rhs) : m_rows(rhs.m_rows), m_cols(rhs.m_cols)
{
data_ptr = create2DArray(m_rows, m_cols);
std::copy(&rhs.data_ptr[0][0], &rhs.data_ptr[m_rows-1][m_cols], &data_ptr[0][0]);
}
Array2D(Array2D&& rhs) noexcept
{
data_ptr = rhs.data_ptr;
m_rows = rhs.m_rows;
m_cols = rhs.m_cols;
rhs.data_ptr = nullptr;
}
Array2D& operator=(Array2D&& rhs) noexcept
{
if (&rhs != this)
{
swap(rhs, *this);
}
return *this;
}
void swap(Array2D& left, Array2D& right)
{
std::swap(left.data_ptr, right.data_ptr);
std::swap(left.m_cols, right.m_cols);
std::swap(left.m_rows, right.m_rows);
}
Array2D& operator = (const Array2D& rhs)
{
if (&rhs != this)
{
Array2D temp(rhs);
swap(*this, temp);
}
return *this;
}
T* operator[](unsigned row)
{
return data_ptr[row];
}
const T* operator[](unsigned row) const
{
return data_ptr[row];
}
void create(unsigned rows, unsigned cols, const T& val = T())
{
*this = Array2D(rows, cols, val);
}
};
int main()
{
try
{
Array2D<double> dPtr(10, 10);
std::cout << dPtr[0][0] << " " << a2[0][0] << "\n";
}
catch (std::exception& ex)
{
std::cout << ex.what();
}
}
I think you should write a simple class to wrap a 1-dim array. Then you can implement a 2-dim array with operator() overloading for getting values and deconstruct func for release the memory. Code as below:
#include <assert.h>
template <typename T>
class Array_2D
{
private:
T *data_inside;
public:
int size[2];
Array_2D(int row, int column);
~Array_2D();
//
T operator()(int index1, int index2){
return data_inside[get_index(index1, index2)];
}
int get_index(int index1, int index2){
if(index1>=0 and index1<size[0] and index2>=0 and index2<=size[1]){
return index1*size[0] + index2;
}else{
assert("wrong index for array!" == "True");
}
}
};
template <typename T>
Array_2D<T>::Array_2D(int row, int column)
{
size[0] = row;
size[1] = column;
data_inside = new T[row*column];
}
template <typename T>
Array_2D<T>::~Array_2D()
{
// 使用析构函数,自动释放资源
delete[] data_inside;
}