I'm currently reading about mixin classes and I think I unerstand everything more or less. The only thing I don't understand is why I don't need virtual functions anymore. (See here and here)
E.g. greatwolf writes in his answer here that virtual functions are not needed. Here is the example: (I just copied the essential parts)
struct Number
{
typedef int value_type;
int n;
void set(int v) { n = v; }
int get() const { return n; }
};
template <typename BASE, typename T = typename BASE::value_type>
struct Undoable : public BASE
{
typedef T value_type;
T before;
void set(T v) { before = BASE::get(); BASE::set(v); }
void undo() { BASE::set(before); }
};
typedef Undoable<Number> UndoableNumber;
int main()
{
UndoableNumber mynum;
mynum.set(42); mynum.set(84);
cout << mynum.get() << '\n'; // 84
mynum.undo();
cout << mynum.get() << '\n'; // 42
}
But what happens now if I do something like this:
void foo(Number *n)
{
n->set(84); //Which function is called here?
}
int main()
{
UndoableNumber mynum;
mynum.set(42);
foo(&mynum);
mynum.undo();
cout << mynum.get() << '\n'; // 42 ???
}
What value does mynum have and why? Does the polymorphism work in foo()?!?
n->set(84); //Which function is called here?
Number::set will be called here.
Does the polymorphism work in foo()?!?
No, without virtual. If you try the code, you'll get an unspecified value because before doesn't be set at all.
LIVE
I compiled your code in VS 2013, and it gives an unspecified number.
You got no constructor in your struct, which means that the variable before is not initialized.
Your code example invokes undefined behaviour, because you try to read from the int variable n while it is not in a valid status. The question is not what value will be printed. Your program is not required to print anything, or do anything that makes sense, although you are likely using a machine on which the undefined behaviour will only present itself as a seeminly random value in n or on which it will mostly appear as 0.
Your compiler likely gives you an important hint if you allow it to detect such problems, for example:
34:21: warning: 'mynum.Number::n' is used uninitialized in this function [-Wuninitialized]
However, the undefined behaviour starts even before that. Here's how it happens, step by step:
UndoableNumber mynum;
This also creates the Number sub-object with an unintialised n. That n is of type int and can thus have its individual bits set to a so-called trap representation.
mynum.set(42);
This calls the derived-class set function. Inside of set, an attempt is made to set the before member variable to the uninitialised n value with the possible trap representation:
void set(T v) { before = BASE::get(); BASE::set(v); }
But you cannot safely do that. The before = BASE::get() part is already wrong, because Base::get() copies the int with the possible trap representation. This is already undefined behaviour.
Which means that from this point on, C++ as a programming language no longer defines what will happen. Reasoning about the rest of your program is moot.
Still, let's assume for a moment that the copy would be fine. What else would happen afterwards?
Base::set is called, setting n to a valid value. before remains in its previous invalid status.
Now foo is called:
void foo(Number *n)
{
n->set(84); //Which function is called here?
}
The base-class set is called because n is of type Number* and set is non-virtual.
set happily sets the n member variable to 84. The derived-class before remains invalid.
Now the undo function is called and does the following:
BASE::set(before);
After this assignment, n is no longer 84 but is set to the invalid before value.
And finally...
cout << mynum.get() << '\n';
get returns the invalid value. You try to print it. This will yield unspecified results even on a machine which does not have trap representation for ints (you are very likely using such a machine).
Conclusion:
C++ as a language does not define what your program does. It may print something, print nothing, crash or do whatever it feels like, all because you copy an unininitialised int.
In practice, crashing or doing whatever it feels like is unlikely on a typical end-user machine, but it's still undefined what will be printed.
If you want your derived-class set to be called when invoked on a Number*, then you must make set a virtual function in Number.
Related
I ran into a nasty bug in some of my code. Here's the simplified version:
#include <iostream>
class A
{
public:
std::string s;
void run(const std::string& x)
{
// do some "read-only" stuff with "x"
std::cout << "x = " << x << std::endl;
// Since I passed X as a const referece, I expected string never to change
// but actually, it does get changed by clear() function
clear();
// trying to do something else with "x",
// but now it has a different value although I declared it as
// "const". This killed the code logic.
std::cout << "x = " << x << std::endl;
// is there some way to detect possible change of X here during compile-time?
}
void clear()
{
// in my actual code, this doesn't even happen here, but 3 levels deep on some other code that gets called
s.clear();
}
};
int main()
{
A a;
a.s = "test";
a.run(a.s);
return 0;
}
Basically, the code that calls a.run() use to be used for all kinds of strings in the past and at one point, I needed the exact value that object "a.s" had, so I just put a.s in there and then some time later noticed program behaving weird. I tracked it down to this.
Now, I understand why this is happening, but it looks like one of those really hard to trace and detect bugs. You see the parameter declared as const & and suddenly it's value changes.
Is there some way to detect this during compile-time? I'm using CLang and MSVC.
Thanks.
Is there some way to detect this during compile-time?
I don't think so. There is nothing inherently wrong about modifying a member variable that is referred by a const reference, so there is no reason for the compiler to warn about it. The compiler cannot read your mind to find out what your expectations are.
There are some usages where such wrong assumption could result in definite bugs such as undefined behaviour that could be diagnosed if identified. I suspect that identifying such cases in general would be quite expensive computationally, so I wouldn't rely on it.
Redesigning the interface could make that situation impossible For example following:
struct wrapper {
std::string str;
};
void run(const wrapper& x);
x.str will not alias the member because the member is not inside a wrapper.
maybe this question will be a bit complicated and maybe i'm missing something of stupid.
I'll try to explain without any source code, because my project is big and i don't know how/where to start.
I have:
bool result = false;
bool* pointer = &result;
these variables are stored in some classes.. (not as in the above code).
When result is created, his address is something like 0x28fddc.
and the pointer variable takes this address.
Suddendly, without any reason (maybe), his address is not 0x28fddc anymore but something like 0x3a8c6e4.
With the pointer variable, i am trying to change the result var by doing:
*result = true;
But obviously, this doesn't work (and it doesn't give me any error). It will not change result var because it's in another address.
I don't know why this happens.
Can you only tell me how could this happen? And i'll try to fix.
(This classes are everytime being updated in some functions with parameters passed by reference).
For example:
void update_class(obj_class &obj);
(These names are only an example).
I hope i've been clear and if not, i'll just delete this topic.
Sorry for bad english but i'm italian :)
EDIT:
Now i'll try to provide some code..
button.h
class button
{
public:
void check_tap(SDL_Event* e);
bool* done;
}
messagebox.h:
class messagebox
{
public:
messagebox();
bool result_ok;
button btn_ok;
}
void check_tap(std::vector<messagebox> &msgbox, SDL_Event* e) {
for(unsigned int k=0; k<msgbox.size(); k++) {
msgbox[k].btn_ok.check_tap(e);
// check_tap is a function that i create for checking if the user is tapping the button with his finger or not. When the user presses the button and leaves it the done variable should become true, but result_ok seems not to be affected because his address here is different. This problem is only in this case using messagebox. I created more other buttons outside and all works perfect.
}
}
messagebox.cpp:
messagebox::messagebox() {
// Initializing things
btn_ok.done = &result_ok;
// Here, btn_ok.done gets the address of result_ok..
}
main.cpp:
std::vector<messagebox> msgbox;
msgbox.push_back(msgbox());
No, the address of variables do not change during their lifetime.
However, storing the address of a variable is problematical if the variable ceases to exist. A simple example is
#include <iostream>
int *p;
void f()
{
int i;
p = &i;
}
int main();
{
f();
std::cout << (void *)p << '\n';
// other code here
f();
std::cout << (void *)p << '\n';
}
In the above case, the two values of p may be the same, or they may differ. This is not because the address of variables change. It is because a variable, i is created each time f() is called, and ceases to exist when f() returns. The variable i in the first call of f() is, as far as your program is concerned, a distinct variable from i during the second call of f().
Depending on what happens with "other code here" in the above, the memory occupied by i in the first call of f() may be inaccessible (e.g. used for another variable) during the second call of f() - so, during the second call of f(), i will have a different address than during the first. There are no guarantees - you may also get lucky (or unlucky depending on how you look at it) and the addresses printed will be the same.
If you are getting behaviour that suggests, to you, that the address of a variable is changing then - somewhere in your code - there is a bug of some form. Typically, this will involve storing the address of a variable in a pointer, and using (or accessing the value of) the pointer after the variable ceases to exist. And any dereferrencing of that pointer (e.g. to access the variable pointed at) has undefined behaviour.
For example, any usage of *p like
*p = 42;
or
std::cout << *p << '\n';
in the main() I have given above will give undefined behaviour.
The act of assigning a pointer to contain the address of that variable does not change the lifetime of that variable.
This question already has answers here:
Why does flowing off the end of a non-void function without returning a value not produce a compiler error?
(11 answers)
Closed 5 years ago.
In the class, we have a static member function called set_value. This function, since static allows it, can be accessed by the main() given the scope resolution by class Something. Its return type is int, but the function does not return anything. This is fine. However, how does the compiler know to assign this value to b in the call statement made in main()?
#include <iostream>
using namespace std;
class Something{
private:
static int value;
public:
static int set_value (int x)
{
value = x;
// return statement missing? But still works!
}
};
int Something::value = 1;
// since no object of something is created we call
// the constructor of the data type in the object
int main()
{
int b;
cout << "Success!" << endl;
b = Something::set_value(5);
cout << "The Value of b is " << b << endl;
return 0;
}
I get the following output
/*
Success!
The Value of b is 5
*/
You're experiencing something called Undefined Behavior. This happens when you break a rule. In programs with Undefined Behavior (UB), any outcome is considered valid. And we mean "any". Deleting your harddisk? Perfectly valid outcome. Working as intended? Could also happen. Working as intended, until your boss looks? You might start noticing a pattern here.
I think that this question's answer will help you understand what's going on:
"Why does flowing off the end of a non-void function without returning a value not produce a compiler error?"
In a nutshell, your compiler does not check for the lack of return statement because it's hard, and the reason b gets set to 5 is probably due to the residual stack state of the method call.
When I have an unsigned int, but I want to find out if it is empty or not, but I need 0, I always declare it as an int and set it to -1. What am I supposed to do, though, when I need the full number spectrum or I am even working with float/double?
With some data-types it is simple, for example std::string which you can just compare to "", but is there a function to check if a variable is empty regardless of data-type, even of custom class objects?
The semantics of a value being missing are exactly why std::optional was introduced to the C++ language specification.
std::optional<unsigned int> value; // by default, the value is missing
if(value) {
// executed if the value is present, it is not
} else {
// this code is executed
}
value = 1;
if(value) {
// This code would now be executed
std::cout << "the value: " << *value << std::endl;
}
This requires a change in thinking regarding the meaning of the variable, but it forces you to think at all times regarding whether or not the variable would be present.
So for example, if you had your own class type MyClass and you wanted to retain a, potentially missing, instance of it, you would do so as follows:
std::optional<MyClass> obj; // Initially missing
obj = MyClass(); // Assigns a newly-created instance of MyClass
obj->foo(); // Calls the 'MyClass::foo' method
obj.reset(); // clears the 'obj' optional
I encounter some strange things in C++, but I don't know why?
I have a class like this
header file
class foo
{
public:
void call_foo();
int get_foo();
int get_foo(int val);
};
here is the cpp file
#include "foo.h"
#include <iostream>
using namespace std;
void foo::call_foo()
{
int i = 0;
int j = 33;
cout << i + j << endl;
cout << "Hello, Foo" << endl;
}
int foo::get_foo(int val)
{
int a = 345;
int rc = val + a;
cout << rc << endl;
return rc;
}
int foo::get_foo()
{
int a = 100;
int d = 23;
int rc = a + d;
cout << rc << endl;
return rc;
}
I using code to test as below
int main()
{
int* val = new int[100];
foo* foo_ptr;
foo_ptr = (foo*)val;
foo_ptr->call_foo();
foo_ptr->get_foo();
foo_ptr->get_foo(100);
delete [] val;
return 0;
}
then i compile and execute it.
clang++ foo.cpp main.cpp
Apple LLVM version 5.0 (clang-500.2.79)
os x 10.9
an int pointer convert to an object pointer, then call it's methods, it work! so weird!
Is there anybody know what is going on?
I wrote an article on my blog about why it works in my understood, Thanks all of you!! about object structure, virtual function table. just Chinese version :)
What you are experiencing is called Undefined Behavior.
Undefined Behavior means "anything can happen." Anything here includes the illusion that your code worked, did something you expected it to do, or didn't do something you expected it to do -- like crash.
Code that evokes Undefined Behavior is always faulty code. You cannot rely on Undefined Behavior, if simply for the reason that you cannot predict what will happen.
Now in this case, the reason why calling the methods might appear to work is because in practice an instance of a class doesn't get it's own copy of the code for each of the non-static methods. Instead, there's one copy of the code that is shared between all instances of foo. The pointer to that code never changes, so when you (incorrectly) resolve a pointer-to-foo and then call one of the methods through that pointer, the actual method you expected to call was actually called. This is all still Undefined Behavior however, and you need to fix your code.
It is undefined behaviour and your program is ill-formed. As far as language specification is concerned, anything could happen.
It just happens to appear to work because no member function access any data that would belong to a particular instance of foo objects. All they do is allocate local data and access cout.
It doesn't work, it has undefined behaviour.
However, the functions aren't virtual, and the object has no data members, so it's likely that your program won't actually touch the invalid memory, and so will have the same effect as calling the functions on a valid object.
Your class has no members and no virtual functions so when you call a member function through any arbitrary pointer it will 'work' since you have a statically bound function call and don't do any memory access that would be invalid. Bad things would happen if you tried to call a virtual function or access a member variable.