Во-первых, заметка о значении исключений.Далее я расскажу о том, что не так с вашим кодом.
Значение выброса исключения в C ++ на английском языке примерно таково: «Я больше не могу справиться с этой ситуацией, я отказываюсьпрямо сейчас, и я надеюсь, что кто-то еще может справиться с этим ".Значение перехвата исключения примерно равно: «Ой, кто-то облажался, но я собираюсь что-то с этим сделать сейчас».Имея это в виду, давайте посмотрим, как это работает, более подробно.
Многие функции имеют предварительных условий , то есть те вещи, которые функция всегда ожидает, чтобы быть истинной, прежде чем она запустится.Например, функция squareRoot(double x)
может иметь предварительное условие, что x
никогда не должно быть отрицательным.Когда предварительное условие функции нарушается, для этой функции естественное время сказать: «Я сдаюсь сейчас, разберись с этим!»поскольку кто-то, кто вызвал эту функцию, делает что-то не так, и squareRoot
не может это исправить.Это может выглядеть следующим образом:
// precondition: x must not be negative
double squareRoot(double x){
if (x < 0.0){
// precondition is violated
throw std::runtime_error("input to squareRoot was negative");
// the function exits here!
// this point in code is not reachable
}
// otherwise, precondition is satisfied, it's safe to continue
...
return result;
}
в другом месте, в дикой природе:
void myFunction(){
double x;
cout << "x = ";
cin >> x;
double y = squareRoot(x);
cout << ", y = " << y << '\n';
}
Здесь, если кто-то вводит отрицательное число, то условия squareRoot
нарушаютсяи это вызывает исключение.Это означает, что squareRoot
не будет возвращать нормально , а также myFunction
.Когда генерируется исключение, все прерывается до тех пор, пока вы не поймаете исключение, которое может произойти далеко за пределами текущей функции.
int main(){
try {
myFunction(); // this might throw
// if myFunction() throws, the following output will not happen!
cout << "Thanks! have a nice day.\n";
} catch (std::runtime_error e) {
// if myFunction() does throw, the error lands right here
cout << "Oops! an error occurred: " << e.what() << '\n';
}
return 0;
}
Важно отметить, что исключения следует генерировать только тогда, когда действительно неправильно.Исключения составляют , а не замена обычной программной логики, и они не замена для проверки таких вещей, как пользовательский ввод.
О пользовательских классах:
Когда вы определяете пользовательский тип класса, если он имеет конструктор копирования, оператор копирования или деструктор, это, вероятно, означает, что ваш класс имеет конфиденциальную информацию, которая требует специальнойзаботиться о том, чтобы убирать и следить.Если вы написали хотя бы одну из этих функций, вам следует реализовать все три из них .В противном случае в вашу программу чрезвычайно легко внедрить утечки памяти, ложное совместное использование данных и все виды нежелательного неопределенного поведения.
Об операторе назначения копирования:
Назначение оператора назначения копирования, как в a = b;
, состоит в том, чтобы после его запуска левый объект должен был логически совпадать с правый объект и правый объект не должны были измениться.Предыдущее состояние левого объекта в этот момент теряется.То, что в точности означает логически, означает вопрос о том, что представляет ваш класс.
В случае простой матрицы, я бы ожидал, что две матрицы будут идентичными, если они имеют одинаковую ширину, высоту и значения для всех элементов.и я бы соответственно реализовал оператор присваивания копии.Например:
Matrix& Matrix::operator=(const Matrix& other){
releaseMemory(); // clean up old data
resize(other.rows, other.cols); // allocate new data
for (int i = 0; i < rows; ++i){
for (int j = 0; j < cols; ++j){
this->data[i][j] = other.data[i][j]; // copy each value
}
}
return *this;
}
Я оставляю вам детали реализации.Вы должны быть в состоянии найти отличные примеры и подробные объяснения о том, как перегрузить операторы, написать свой собственный конструктор и деструктор копирования и о хорошей практике в целом, прочитав хорошую книгу по C ++ .