一個類 A 有成員 double[][],我希望直接能在對象上進行類似這樣的操作:

A a;

double x = a[0][0];

該怎麼實現?


需要一個 helper 類,大約是:

class ARowHelper {
friend class A;
A a_;
size_t r_;
ARowHelper(A a, size_t r) : a_(a), r_(r) {}
public:
double operator[](size_t c) { return a_.data_[r_][c]; }
};

class A {
double** data_;
public:
ARowHelper operator[](size_t r) { return ARowHelper(*this, r); }
};

實際上還要考慮 const-ness 等問題。


operator[]返回一個double*或者不就夠了,使用時[][]裏的第二個[]是指針自帶。

不過存多維數組的話我還是推薦扁平化……

C++怎麼實現效率儘可能高地帶邊界檢查的多維數組模板類??

www.zhihu.com圖標

與其重載[][]不如重載()。你所需要的操作它都能滿足,而且也不失優雅。

[][]這種表達,很容易聯想到一種數據類型,那就是矩陣:Matrix。

而關於Matrix該如何優雅的重載運算符。isocpp官方恰好有個demo,那就是重載 operator () 而不是[]:

class Matrix {
public:
Matrix(unsigned rows, unsigned cols);
double operator() (unsigned row, unsigned col); // Subscript operators often come in pairs
double operator() (unsigned row, unsigned col) const; // Subscript operators often come in pairs
// ...
~Matrix(); // Destructor
Matrix(const Matrix m); // Copy constructor
Matrix operator= (const Matrix m); // Assignment operator
// ...
private:
unsigned rows_, cols_;
double* data_;
};
inline
Matrix::Matrix(unsigned rows, unsigned cols)
: rows_ (rows)
, cols_ (cols)
//, data_ ← initialized below after the if...throw statement
{
if (rows == 0 || cols == 0)
throw BadIndex("Matrix constructor has 0 size");
data_ = new double[rows * cols];
}
inline
Matrix::~Matrix()
{
delete[] data_;
}
inline
double Matrix::operator() (unsigned row, unsigned col)
{
if (row &>= rows_ || col &>= cols_)
throw BadIndex("Matrix subscript out of bounds");
return data_[cols_*row + col];
}
inline
double Matrix::operator() (unsigned row, unsigned col) const
{
if (row &>= rows_ || col &>= cols_)
throw BadIndex("const Matrix subscript out of bounds");
return data_[cols_*row + col];
}

int main()
{
Matrix m(10,10);
m(5,8) = 106.15;
std::cout &

引用自:https://isocpp.org/wiki/faq/operator-overloading#matrix-subscript-op

[][] 的通過中間類還能實現。假如你日後需要[][][]呢?這並不稀奇,比如表示更高維的向量空間。

operator ()比之其他operator的一大好處就是不限制參數個數,並且()看著也並不突兀。


←_← 想好了,支持a[0][0]就意味著(a[0])[0][](auto r) { return r[0]; }(a[0])都要有同樣的語義。尤其是後一個,用戶可能會拿r做各種各樣的操作,作為實現者要想好哪些操作有什麼後果。

如果不想承受這種心智負擔的話,還是不要支持這種寫法為好。a.at(0, 0)也沒多長。

支持a[0][0]的方式其他答主都解釋過了,就是讓operator[]返回一個proxy。


模板大法好,可以套娃套一個任意維度的出來。上星期剛好寫了一個。

用起來像這樣:

支持直接用index訪問,和(只讀)插值訪問。

代碼:

#pragma region Table

#pragma region ToolStructs
template&
struct Multiplicative;
template&
struct Multiplicative& {
enum { value = First * Multiplicative&::value };
};
template&
struct Multiplicative& {
enum { value = First };
};
template&
struct Count;
template&
struct Count& {
enum { value = Count&::value + 1 };
};
template&
struct Count& {
enum { value = 1 };
};
#pragma endregion

template&
struct TableBlock;
template&
struct TableBlock&
{
DataType* ptr;
DataType operator[](const int x) {
return *(ptr + x);
}
DataType Evaluate(const double* coord) {
double x = coord[0];

int size_sub_1 = size - 1;
double c_x = max(0.0, min(x, 1.0));
int index = c_x * size_sub_1;
int index_add_1 = min(index + 1, size_sub_1);

return lerp(ptr[index], ptr[index_add_1], c_x * size_sub_1 - index);
}
TableBlock(DataType* ptr) : ptr(ptr) {}
};
template&
struct TableBlock&
{
DataType* ptr;
TableBlock& operator[](const int x) {
int size_sub_1 = size - 1;
auto offset = max(0, min(x, size_sub_1)) * Multiplicative&::value;
return TableBlock&(ptr + offset);
}
DataType Evaluate(const double* coord) {
double x = coord[0];
int size_sub_1 = size - 1;
double c_x = max(0.0, min(x, 1.0));
int index = c_x * size_sub_1;
int index_add_1 = min(index + 1, size_sub_1);

DataType res0 = operator[](index).Evaluate(coord+1);
DataType res1 = operator[](index_add_1).Evaluate(coord+1);

return lerp(res0, res1, c_x * size_sub_1 - index);
}

TableBlock(DataType* ptr) : ptr(ptr) {}
};

template&
struct Table
{
TableBlock&::value, size, rest...&> table;
Table() : table(nullptr) {}
~Table() {
if (table.ptr != nullptr)
delete(table.ptr);
}
void Init() {
if (table.ptr != nullptr) delete(table.ptr);
table.ptr = new DataType[Multiplicative&::value];
}
TableBlock&::value - 1, rest...&> operator[](const int x) {
return table[x];
}

DataType operator[](const initializer_list& coord) {
return table.Evaluate(coord.begin());
}

void ClearWith(const DataType data) {
for (int i = 0; i &< Multiplicative&::value; i++) {
table.ptr[i] = data;
}
}

void Save(string name) {
ofstream ofs("./" + name, ios::out | ios::binary);

string format = "(%d";
for (int i = 1; i &< Count&::value; i++) {
format += ",%d";
}
format += ")
";
char tmp[99999];
sprintf(tmp, format.c_str(), size, rest...);
format = string(tmp);

ofs.write((char*)format, sizeof(format));
ofs.write((char*)table.ptr, sizeof(DataType) * Multiplicative&::value);

ofs.close();
}

Table(string name) : table(new DataType[Multiplicative&::value]) {
ifstream ifs("./" + name, ios::in | ios::binary);

string format = "(%d";
for (int i = 1; i &< Count&::value; i++) {
format += ",%d";
}
format += ")
";
char tmp[99999];
sprintf(tmp, format.c_str(), size, rest...);
format = string(tmp);

ifs.read((char*)format, sizeof(format));
ifs.read((char*)table.ptr, sizeof(DataType) * Multiplicative&::value);

ifs.close();
}

void operator=(Table& c) {
table.ptr = c.table.ptr;
c.table.ptr = nullptr;
}
};
template&
struct Table&
{
TableBlock& table;
Table() : table(nullptr) {}
~Table() {
if (table.ptr != nullptr)
delete(table.ptr);
}
void Init() {
if (table.ptr != nullptr) delete(table.ptr);
table.ptr = new new DataType[size];
}
DataType operator[](const int x) {
return table[x];
}

DataType operator[](const initializer_list& coord) {
return table.Evaluate(coord.begin());
}

void ClearWith(const DataType data) {
for (int i = 0; i &< size; i++) { table.ptr[i] = data; } } void Save(string name) { ofstream ofs("./" + name, ios::out | ios::binary); string format = "(%d)"; char tmp[99999]; sprintf(tmp, format.c_str(), size); format = string(tmp); ofs.write((char*)format, sizeof(format)); ofs.write((char*)table.ptr, sizeof(DataType) * size); ofs.close(); } Table(string name) : table(new DataType[Multiplicative&::value]) {
ifstream ifs("./" + name, ios::in | ios::binary);

string format = "(%d)";
char tmp[99999];
sprintf(tmp, format.c_str(), size, rest...);
format = string(tmp);

ifs.read((char*)format, sizeof(format));
ifs.read((char*)table.ptr, sizeof(DataType) * size);

ifs.close();
}

void operator=(Table& c) {
table.ptr = c.table.ptr;
c.table.ptr = nullptr;
}
};

#pragma endregion


不建議這麼做。因為 [] [] 是兩個獨立的操作符,意味著拿[]的返回值再進行一次operator[]操作。理論上可以通過一個代理來實現,一旦這麼搞,就必然意味著會有人拿單獨的 [] 調用出那個中間代理用於做一些你想像不到的用途,事態可能變得難以控制。

變通的辦法是把雙下標做成單一調用。

比如說 a[{0,0}] (重載 [公式] 接收一個常數數組作為參數),或者比如說 a(0,0) (重載 [公式] 接收兩個數作為參數)。


我覺得如果沒有很特殊的需要的話,你完全沒必要重載[][]啊,你重載[]然後返回一個double*不就好了


class A{
public:
double mArray[2][2];
double* operator[] (int index){
return mArray[index];
}
};

木考慮越界,方式上可以這樣實現(我在自己機器上可以通過)


a[0]返回一個A_類型的值a_,a_[0]再返回最終值


其實就是實現 operator 【】就好了啊


建立一個中間對象


推薦閱讀:
相關文章