Operator overloading c++ advice - c++

I am struggling to get my head around operator overloading. In this case the + operator I have and example of what I have tried, any help would be greatly appreciated.
I am getting an error which says "invaild use of 'class Matrix' I am unsure on how to fix this how can I add these two Matrix objects together?
Matrix Matrix::operator+(const Matrix& rhs){
return Matrix(Matrix + rhs.Matrix());
}
Matrix::Matrix(int MM, int NN){
M = MM;
N = NN;
data = new double[M * N];
for ( int i =0; i < M; i++)
{
for (int j = 0; j < N; j++)
{
data[i* N+j] = (double) 1000 + i*N+j;
// cout << data[i*N+j] <<"\t";
}
//cout <<"\n";
}
cout << "Matrix Constructor... (code to be implemented here!!!)\n";}
Thanks

rhs is a Matrix
calling a constructor like a method is very illegal
in Matrix + rhs, Matrix is not an identifier
Once you get your identifiers straightened out, *this + rhs is equivalent to this->operator+(rhs). It should be obvious from this that all you've done here is create an infinite recursion.

jpm's answer is very important to look at. Once you've fixed these things, you can look at mine.
Essentially, an operator overload is no different than any other function.
So, in the case of:
Matrix Matrix::operator+(const Matrix& rhs)
What you're really doing there is saying: add rhs to the current matrix, and return a new matrix. Your overload should not alter the current matrix. Help yourself and use a constant:
Matrix Matrix::operator+(const Matrix& rhs) const
A matrix addition like that should first check if the matrices have the same dimensions so that you can add them together, then loop through all "cells" and add them, and create a matrix out of that. For that, I'm guessing you'll need a second constructor, something like:
Matrix::Matrix(int MM, int NN, double values[])
{
M = MM;//TODO: change to width
N = NN;//TODO: change to height
data = new double[M*N];
for(int i < 0; i < M; i++)
for(int j < 0; j < N; j++)
data[i * N+j] = values[i * N+j];
}

return Matrix(Matrix + rhs.Matrix());
^^^^^^ ^^^^^^
You're using a type name (Matrix) where there should be an expression - that's what the compiler is complaining about. You're also trying to call the constructor on an existing object, which is invalid (and nonsensical). You also seem to be trying to implement operator+ in such a way that it calls itself; if you do somehow make it compile, then it will cause a stack overflow due to infinite recursion.
Perhaps the easiest way to implement addition is to implement operator+= to add one matrix to an existing one, and then implement operator+ in terms of that:
Matrix & Matrix::operator+=(Matrix const & rhs) {
// Perform addition here
for (int i = 0; i < N*M; ++i) {
data[i] += rhs.data[i];
}
return *this;
}
// This can be a non-member function
// Pass "lhs" by value to get a copy, then modify and return that.
Matrix operator+(Matrix lhs, Matrix const & lhs) {
return lhs += rhs;
}
// Or if you really want a member function for some reason
Matrix Matrix::operator+(Matix const & rhs) const {
return Matrix(*this) += rhs;
}
This does require you to have a correct copy constructor - since you're allocating memory yourself in the constructor, and presumably deallocating it in the destructor, you must implement a copy-constructor to correctly allocate new memory (per the Rule of Three), otherwise you'll get a double-deletion after copying a matrix.

Related

Why override operators aren't working with pointer?

I need to write a class Matrix with override operators + - * = and I've got some code that is works, but there is error.
//Matrix.h
template <class T>
class Matrix
{
public:
Matrix(int rows, int columns);
Matrix(const Matrix<T> &m);
Matrix<T>& operator=(Matrix<T>& m);
Matrix<T> operator+(Matrix<T>& m) const;
Matrix<T>* operator*(Matrix<T>* m);
};
template <class T> Matrix<T>& Matrix<T>::operator*(Matrix<T>& m) {
Matrix<T>* newMatrix = new Matrix<T>(rowCount, m.colCount);
for (int i = 0; i < rowCount; ++i)
{
for (int j = 0; j < m.colCount; ++j)
{
newMatrix->data[i][j] = 0;
for (int k = 0; k < colCount; ++k)
newMatrix->data[i][j] += data[i][k] * m.data[k][j];
}
}
return *newMatrix;
}
And override operators work fine in this code
Matrix<int> matrix(2, 2);
matrix = matrix + matrix;
//and other operators work fine here
But here it gives an error during compilation
Matrix<int>* matrix = new Matrix<int>(2, 2);
matrix = matrix + matrix;
matrix = matrix * matrix;
//etc
error
error C2804: binary "operator +" has too many parameters
Type information is crutal in C++.
This:
Matrix<int> matrix(2, 2);
matrix = matrix + matrix;
Here the type of matrix is Matrix. You have defined what the operator + for the type Matrix so this works fine.
This second one is different:
Matrix<int>* matrix = new Matrix<int>(2, 2);
matrix = matrix + matrix;
Here the type of matrix is Matrix*. Notice the star on the end of Matrix (this makes it a Matrix Pointer). This is a different type than above. You have not defined what operator + does for Matrix* so the compiler looks at its default operations and finds something close but not exact enough and generates an appropriate error message that tries to help.
To use the operator + you defined above you need to make sure the types of the values are Matrix and NOT Matrix*. You can covert a Matrix* into a Matrix by dereferencing the pointer via operator *.
Matrix<int>* matrix = new Matrix<int>(2, 2);
(*matrix) = (*matrix) + (*matrix);
Here: (*matrix) dereferences the Matrix* object and you get a Matrix. This can now correctly be applied to the operator + you defined above.
But saying all that.
That explains whay your problem is, but I don't think you actually want (or need) to use new in this context.
Dynamically allocating memory like this is probably not the correct way to implement this. It is usually better to use automatic variables (as this makes memory management easier).
template <class T>
Matrix<T> Matrix<T>::operator*(Matrix<T>& m)
// I also removed the & from the return value
// So that the value is copied out of the function.
// because with dynamic allocation the result will disappear
// after the function exits.
// Note: Though the matrix may be officially copied out
// the compiler is likely to optimize away this copy.
// and when you upgrade your class with move semantics
// then it will definitely be moved.
{
Matrix<T> newMatrix(rowCount, m.colCount);
// Remove the Pointer from the above line.
// And fix the -> into . in the code below.
for (int i = 0; i < rowCount; ++i)
{
for (int j = 0; j < m.colCount; ++j)
{
newMatrix.data[i][j] = 0;
for (int k = 0; k < colCount; ++k)
newMatrix.data[i][j] += data[i][k] * m.data[k][j];
}
}
return newMatrix;
// Now you can remove the * from the return value.
// The code works exactly the same.
// But you have not leaked the memory you allocated with `new`
}

Call an Operator+ function inside the += operator function in C++

I have got this overloading + operator function below and I have to write another overloading += operator function. I was wondering if I can just call the + operator function inside the =+ operator function since essentially both the function are doing the same thing. If so, then what does the syntax for it look like?
Below is my + operator function. I'm trying to add 2 Dynamically allocated matrices.
Matrix Matrix::operator + (const Matrix & orig) const
{
int length = columns * rows;
try
{
if (rows != orig.rows || columns != orig.columns)
{
throw 1;
}
}
catch (int c)
{
if (c == 1)
{
cout << "Error. Check Matrix dimensions, they do not match." << endl;
}
}
Matrix x(rows, columns);
for (int i = 0; i < length; i++)
{
x.data[i] = data[i] + orig.data[i];
}
return x;
}
void Matrix::operator += (const Matrix & orig)
{
//just call the + operator function!
}
Yes you can, you could make your function return a Matrix:
*this = *this + orig;
return *this;
You can simply call the + operator as you would do it everywhere else. One operand here is of course the parameter orig while the other is the object you call the operator on itself, which is *this.
So you could just write:
*this = *this + orig
Whether it is wise to define operator+= using operator+ or if its better to do it the other way round is up to you and might depend on your implementation.
However it is usually a good idea to define the += operator as
Matrix& Matrix::operator+= (const Matrix & orig)
since you can then do things like
mat += otherMat += otherMat2;
for this, just return *this as Stefan Giapantzakis already pointed out.
It is easy: just do *this = *this + other.
However, often it is a better idea to write operator+= fully, then write operator+ in terms of += like this:
Matrix& Matrix::operator+=(const Matrix &rhs) {
try {
if (rows != orig.rows || columns != orig.columns)
throw "Error. Check Matrix dimensions, they do not match.";
} catch (char const* msg) {
std::cout << msg << std::endl;
}
int length = columns * rows;
for (int i = 0; i < length; i++) {
data[i] += orig.data[i];
}
return *this;
}
friend Matrix operator+(Matrix lhs, Matrix const& rhs){
lhs += rhs;
return std::move(lhs);
}
which, as a bonus, reduxes a+b+c+d into creating exacly one matrix that is not move constructed.

Program crashes at: (1) matrix multiplication; and (2) failed matrix addition/subtraction

In short, I was assigned the task of creating a class that dynamically allocates memory to form a matrix of int values.
Part of the class are member functions that perform the basic matrix calculations -- addition, subtraction, and multiplication. Everything compiles (on my end at least), but when I used a driver to test the multiplication portion, it keeps crashing.
I'm using Codeblocks as my IDE and haven't had much luck with the debugger there in trying to figure it out. It appears that the calculation completes (with correct values), but then things go horribly wrong somewhere.
For clarity, each object of the Matrix class has the following member data:
private:
int rows;
int cols;
int **element;
Below is a snippet of the implementation file in which the overloaded operator* is fleshed out. The portion where temp.element[i][x] is set to '0' before the loop performing the multiplication is commented out because the default constructor already sets all values to '0' -- which I had forgotten when I put it in originally. It didn't work when I didn't have it commented out either.
In testing, I used one 2x3 array and one 3x2 array.
Matrix Matrix::operator*(const Matrix &aMatrix) const
{
if(cols == aMatrix.rows)
{
Matrix temp(rows, aMatrix.cols);
for(int i = 0; i < rows; i++)
{
for(int x = 0; x < aMatrix.cols; x++)
{
//temp.element[i][x] = 0;
for(int n = 0; n < cols; n++)
{
temp.element[i][x] += (element[i][n]
* aMatrix.element[n][x]);
}
}
}
return temp;
}
else
{
cerr << "Matrix multiplication failed -- incompatible matrix sizes."
<< endl;
return *this;
}
}
Upon trying to go through the code and find errors, I started re-checking the other functions I had. It appeared that both addition and subtraction worked, but the program would close if the matrices were incompatible (ie. trying to add a 2x3 and a 4x4).
Below is the snippet for the addition (subtraction is almost identical just '-' instead of '+' in the final loops.
Matrix Matrix::operator+(const Matrix &aMatrix) const
{
if(rows == aMatrix.rows && cols == aMatrix.cols)
{
Matrix temp(rows, cols);
for(int i = 0; i < rows; i++)
{
for(int x = 0; x < cols; x++)
{
temp.element[i][x] = element[i][x] + aMatrix.element[i][x];
}
}
return temp;
}
else
{
cerr << "Undefined matrix addition -- matrices are different sizes."
<< endl;
return *this;
}
}
Any help or insight is appreciated. Thanks.
EDITED: Added overloaded assignment operator, copy constructor, and destructor code.
Below is the overloaded assignment operator:
Matrix Matrix::operator=(Matrix aMatrix)
{
if(this != &aMatrix)
{
for(int i = 0; i < rows; i++)
{
delete [] element[i];
element[i] = NULL;
}
delete [] element;
rows = aMatrix.rows;
cols = aMatrix.cols;
element = new int* [rows];
for(int i = 0; i < rows; i++)
{
element[i] = new int [cols];
for (int x = 0; x < cols; x++)
{
element[i][x] = aMatrix.element[i][x];
}
}
}
return *this;
}
Below is the copy constructor:
Matrix::Matrix(const Matrix &aMatrix)
{
rows = aMatrix.rows;
cols = aMatrix.cols;
element = new int* [rows];
for(int i = 0; i < rows; i++)
{
element[i] = new int [cols];
for (int x = 0; x < cols; x++)
{
element[i][x] = aMatrix.element[i][x];
}
}
}
Destructor:
Matrix::~Matrix()
{
for(int i = 0; i < rows; i++)
{
delete [] element[i];
element[i] = NULL:
}
delete [] element;
element = NULL;
}
You posted a copy constructor and assignment operator. Your assignment operator has 4 major issues:
You should pass the parameter by const reference, not by value.
You should return a reference to the current object, not a brand new object.
If new throws an exception during assignment, you've messed up your object by deleting the memory beforehand.
It is redundant. The same code appears in your copy constructor.
You can rewrite the assignment operator in terms of the copy constructor and alleviate these issues:
#include <algorithm>
//...
Matrix& Matrix::operator=(const Matrix& aMatrix)
{
Matrix temp(aMatrix);
swap(*this, temp);
return *this;
}
void Matrix::swap(Matrix& left, Matrix& right)
{
std::swap(left.rows, right.rows);
std::swap(left.cols, right.cols);
std::swap(left.element, right.element);
}
You just need to add the swap function to your Matrix class (probably as a private) function.
The code above uses the copy/swap idiom, and alleviates all of the issues I mentioned. This will work provided that your copy constructor and destructor are written properly. There are many threads on SO that talk about this idiom, but basically what is happening is this:
Create a temporary object from the passed-in object. If there is a problem with new throwing, your this doesn't get messed up.
Swap out this's members with the temporaries members. This refreshes the this object with the temporary's data, and gives the temporary the old data we don't need any more.
Let the temporary die with the old stuff we gave it from this.
Return this.
As to the other aspects, if the two Matrices cannot be multiplied, then just throw an exception. Do not return a bogus or confusing Matrix object that doesn't really reflect what happened.
In addition, you should first write operator += and *=. Why? Because implementing operator + and * can be done in terms of += and *=, respectively, plus you get the added bonus of having += and *= available.
For example:
Matrix& Matrix::operator+=(const Matrix &aMatrix)
{
if(rows != aMatrix.rows || cols != aMatrix.cols)
throw SomeException;
for(int i = 0; i < rows; i++)
{
for(int x = 0; x < cols; x++)
element[i][x] += aMatrix.element[i][x];
}
return *this;
}
Matrix Matrix::operator+(const Matrix &aMatrix)
{
Matrix temp(*this);
temp += aMatrix;
return temp;
}
So if the Matrix sizes are an issue, operator + and += will throw an exception. Note how operator + is implemented in terms of +=. Take the same approach to *, *=, and also - and -=.
You need to figure out what the syntax and semantics are of these operations. It looks from your example as though you want something like:
Matrix lhs = <something>;
Matrix rhs = <something>;
Matrix result = lhs * rhs;
to "just work". While this might be nice syntactically, you need to account for what happens if lhs and rhs can't be multiplied (or added, etc.) You also need to worry about setting up the result properly. You can't just return a "struct consisting of number of rows, columns and an array of pointers" and expect not to run into horrific memory management problems.
What will be responsible for making the product disappear? When it goes out of scope? C++ is not a very good language for linear algebra.
The first decision that you need to make is whether you intend to throw an exception for invalid operations, or return some indication of what went wrong, perhaps bool and a const char * to an error message.
Then you probably should try writing a function taking a result reference and 2 operand references, and just get the basic algebra right, if the matrices support the operation. (I have not mentioned yet that your Matrix is an integer matrix only.)
In "real life", you would probably try to find an open source standard solution that manages all of this, because numerical operations need to be managed very carefully.
One might even go so far as to suggest that this is a problem that has already been capably solved decades ago in Fortran, and to this day, if performance is an issue, there is a good chance that the underlying calculations might be in Fortran, or a library that was a port from C.

Error 2 error C2679: binary '/' : no operator found which takes a right-hand operand of type (or there is no acceptable conversion)

I have some problem about operator overloading. I looked everywhere but couldn't find a proper solution for this error. Here is some parts of my code :
Matrix<type> Matrix<type>::operator/(const Matrix& denom){
if(num_of_rows != denom.num_of_rows || num_of_cols != denom.num_of_cols)
throw string("Unable to divide (Different size).");
if(denom.contains(0))
throw string("Unable to divide (Divide by zero).");
for(int i = 0; i < num_of_rows; i++)
for(int j = 0; j < num_of_cols; j++)
values[i][j] /= denom.values[i][j];
// I KNOW THIS IS NOT HOW TO DIVIDE TWO MATRICES
return *this;
}
void Matrix<type>::operator=(const Matrix& m) const {
delete [][] values;
num_of_rows = m.num_of_rows;
num_of_cols = m.num_of_cols;
values = new type*[num_of_rows];
for(int i = 0; i < num_of_rows; i++){
*(values + i) = new type[num_of_cols];
for(int j = 0; j < num_of_cols; j++)
values[i][j] = m.values[i][j];
}
}
And this is the Matrix class and the constructor takes 2 arguments :
class Matrix{
private:
type** values;
int num_of_rows, num_of_cols;
public:
Matrix(){}
Matrix(int, int);
type getElement(int, int);
void print();
bool contains(type);
Matrix<type> operator/(const Matrix&);
void operator=(const Matrix&) const;
};
template <class type>
Matrix<type>::Matrix(int rows, int cols){
values = new type*[rows];
num_of_rows = rows;
num_of_cols = cols;
for(int i = 0; i < rows; i++){
*(values + i) = new type[cols];
for(int j = 0; j < cols; j++){
type random = (type)rand() / 3276.71;
values[i][j] = random;
}
}
}
And this piece of code in main gives this error :
srand(time(NULL));
Matrix<int> m1(3,5); // creating some objects
Matrix<double> m2(3,5); // matrices’ elements are assigned randomly from 0 to 10
Matrix<double> m3(5,5);
Matrix<double> m4(5,6);
if(!(m2.contains(0))){
Matrix<double> m8(3,5);
m8=m1/m2; // THIS LINE GIVES ERROR
m8.print();
}
m1 has type Matrix<int>, so when looking up for a suitable overload of operator/ we find:
Matrix<int> Matrix<int>::operator/(const Matrix& denom);
Note that the parameter type Matrix here makes use of the so-called injected class name. That means Matrix stands in this case for Matrix<int>, since that's the (template) class in question. However m2, the argument to the call to operator/, has type Matrix<double>. There is no suitable conversion from Matrix<double> to Matrix<int> so the call is invalid.
A possible fix is to change operator/ to also be a template:
// Declared inside Matrix<type>:
template<typename Other>
Matrix& operator/=(Matrix<Other> const& other);
(I also took the liberty of fixing the operator to better reflect what it's actually doing.)
However you'll then run into the problem that you're using a Matrix<int> (the result of the call to operator/) to assign to m8 which has type Matrix<double>. So perhaps you need an operator= to do a conversion (in which case I'd recommend a converting constructor as well, or perhaps even just a converting constructor without a converting operator=).
The error message quite clearly states that you haven't defined a division operator taking the two types as arguments you are passing. Looking at the code excerpt this is the case: There is an operator taking two Matrix<T> but none taking aMatrix<T1> and a Matrix<T2> (for different type T1 and T2).
BTW, what is your question?

Bad Access Error in 2D-Array (Matricies)

I have a little bit of a problem... I understand what a EXC_BAD_ACCESS error is and I generally know how to fix it but this one has got me completely stuffed. I have this all within a class, here is one method:
double Matrix::get_element(int r, int c) const {
//Retrieve the element at row r and column c
//Should not modify the value stored in Matrix but return a double copy of the value
double currentValue = matrix[r][c];
return currentValue;
}
Now, I have another piece of my code that calls this method:
std::string Matrix::to_string() const {
std::string result;
double current;
Matrix working = *this;
std::ostringstream oss;
oss << "[";
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
current = 0.0;
current = working.get_element(i, j);
oss << " " << current << " ";
}
oss << "; ";
}
oss << "]";
result = oss.str();
return result;
}
I know that the working object has 3 rows and 3 cols at the point where working.get_element(i, j); is called. The variable list shows me just before the get_element() method, that both rows and cols are set to 3. In the method, I'm able to get the value at get_element(0, 0) but not get_element(0, 1).
I can't see why this is the case... Anyone know why or require more of my code to understand why these methods are being called?
EDIT:
Here is the header file:
class Matrix {
private:
//Any variables required
int rows;
int cols;
double **matrix;
public:
Matrix(); //Working M
~Matrix(); //Working M
Matrix(int r, int c); //Working M
int getRows();
int getCols();
void set_element(int r, int c, double val); //Working M
double get_element(int r, int c) const; //Working M
void clear(); //Working M
bool is_empty(); //Working M
bool is_identity(); //Working M
const Matrix transpose(); //Working M
int minorMat(double **dest, const int row, const int col, int order); //Working M
double get_determinent(); //Working M
double higherDeterminents(int order); //Working M
const Matrix operator+(const Matrix &rhs); //Working M
const Matrix operator-(const Matrix &rhs); //Working M
const Matrix operator*(const Matrix &rhs);
bool operator==(const Matrix &rhs); //NOT assessed
const Matrix operator*(const double &rhs);
const Matrix operator/(const double &rhs);
Matrix & operator=(const Matrix &rhs);
std::string to_string() const;
};
Do ignore the comments sorry. And this is the constructors/destructors:
Matrix::Matrix() {
//Basic Constructor
rows = 1;
cols = 1;
matrix = new double*[rows];
for (int i = 0; i < rows; ++i) {
matrix[i] = new double[cols];
}
}
Matrix::~Matrix() {
//Basic Deconstructor
for (int i = 0; i < rows; ++i) {
delete[] matrix[i];
}
delete[] matrix;
rows = NULL;
cols = NULL;
matrix = NULL;
}
Matrix::Matrix(int r, int c) {
//Empty matrix (all 0's) with r rows and c columns, if they are -ve, set to 1
rows = r;
cols = c;
if (cols < 0)
cols = 1;
if (rows < 0)
rows = 1;
matrix = NULL;
matrix = new double*[rows];
for (int i = 0; i < rows; i++) {
matrix[i] = new double[cols];
}
}
EDIT2:
Matrix & Matrix::operator=(const Matrix &rhs) {
//rhs is matrix to be copied
//rhs compied into Matrix called on
double toCopy;
for (int i = 0; i < rhs.rows; i++) {
for (int j = 0; j < rhs.cols; j++) {
toCopy = rhs.get_element(i, j);
this->set_element(i, j, toCopy);
}
}
return *this;
}
It is impossible for us to say when you do not state how you declare and initialize the matrix element. Using something like that in your CTOR should be fine:
class Matrix {
float matrix[3][3];
...
}
Don't forget to initialize it in your CTOR to something that makes sense.
Btw: why do you do this: Matrix working = *this; ?? You could simply this->get_element(i, j); instead, which would not invoke copying of your whole object. [1]
EDIT: Update since you updated your answer. You should be careful with your copy CTORs and operator=() statements. It is easily possible to make double deletes or something ugly like that.
EDIT2: I think the problem is this line:
Matrix working = *this;
You are creating a new copy working of your this object. But working gets initialized with only 1 column and 1 row (as defined in your standard CTOR). I'm not sure if you are checking the bounds when calling set_element or get_element so I guess you are writing over the bounds of your arrays.
I think the best idea is to just remove the line Matrix working = *this; and to adhere to my tip in above:
this->get_element(i, j); in std::string Matrix::to_string() const.
Your Matrix(int r, int c) allocates memory for matrix, but leaves the values it points to uninitialized. Add something like this to the constructor:
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
this->set_element(i, j, 0);
}
}
Doing like this:
int main()
{
Matrix m(3,3);
std::cout << m.to_string();
}
Output:
[ 0 0 0; 0 0 0; 0 0 0; ].
Same thing in the default constructor:
Matrix::Matrix() {
//Basic Constructor
rows = 1;
cols = 1;
matrix = new double*[rows];
for (int i = 0; i < rows; ++i) {
matrix[i] = new double[cols];
}
}
You allocated memory, but wherever matrix[0][0] points to, it's uninitialized garbage value. Do something like matrix[0][0] = 0; or whatever default value you want it to have.
Hope that helps.
Your class is violating the "big three" rule. If a class has one of a destructor, assignment operator or copy constructor then it most probably you need to have all three.
In your case you have a destructor, but no assignment operator or copy constructor, and this is going to create an UB condition when you do Matrix working = *this.
By the way there are two other problems with your code
From the comment seems that you think that new double[size] will initialize the elements to 0 and this is not true.
Most of your code is technically very bad. Your matrix class would be much easier (less code) to implement correctly using std::vector instead of pointers and dynamic memory. Of course if this is just an exercise then it make sense to avoid using std::vector.
By the way if you never heard about the "big three" rule chances are that you're trying to learn C++ with experimentation instead that by reading.
With C++ this is not a smart move... logic can be used as substitute for study if 1) the topic is highly logical, 2) if you can be told when you get it wrong.
Instead C++ is very very complex and in a few place is also quite illogical (for historical reasons) so there are parts in which logic will simply misguide you.
Moreover when you make a mistake in C++ you don't get in general an error message, but "Undefined Behavior". This basically makes very very hard to learn C++ with experimentation because even wrong code may apparently work. It is also very easy to write code that looks good and that is instead quite wrong for subtle reasons.
Instead of just experimenting you should grab a good book and read it cover to cover...