[.net] what's the purpose of virtual keyword again?

Started by
9 comments, last by RipTorn 17 years, 10 months ago
okay, i've been programming for several years now with OO concepts. but im learning C#, and although it seems like a breeze (just like Java plus C++), i seem to be back-sliding and asking fundamental questions. like virtual functions. why do we need to explicity declare a function as virtual (or a deriving class function as override)? the answer, i know, is so we can use polymorphism and ultimately invoke the derive class' methods over the base class'. BUT...the statement directly above can be made possible without the use of the virtual/override keywords, no? what the heck am i missing here?! argh!
Advertisement
http://www.jaggersoft.com/pubs/AProgrammersOverviewOfCSharp.htm#inheritance
http://www.extremetech.com/article2/0,1697,1164819,00.asp

edit: to be more helpful.

virtual in C#, just like C++, allows the program at runtime to pick the right reference or method at runtime. you would use the virtual keyword on the function of the base class. afterward use override on the function of the derived class. now "sealed override" not that experienced to fully understand that yet.

Beginner in Game Development?  Read here. And read here.

 

Quoting Alpha's link:

Quote:Unlike Java (and like C++) by default C# methods, indexers, properties, and events are not virtual.


If you redefine a non-virtual method in a derived class, you are hiding the base's, not overriding it, meaning that the method will be selected based on the static type of the variable, not its dynamic type.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
thanks for the insights.
one more thing...

if suppose both my base class and my derived class has a virtual draw() function,

this means the base draw() is "hidden", right?

Your base class's function should have the keyword virtual and the derived class's implementation of that function should have the keyword override.

class SomeBaseClass{    public virtual void Foo()    {        System.Console.WriteLine("Called SomeBaseClass's Foo!");    }}class SomeDerivedClass : SomeBaseClass{    public override void Foo()    {        System.Console.WriteLine("Called SomeDerivedClass's Foo!");    }}
Rob Loach [Website] [Projects] [Contact]
No, it is overriden. C# should flag it as an error if you simply declared the derived method 'virtual'.

"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
It's an detail to determine how the compiler implements that function call. It's basically an extra pointer indirection. But to make a short story long, here's an example:
// C++ with no virtual#include <iostream>class base {	protected:		int value;	public:		void print() const {			std::cout << "base: " << value << std::endl;		}		base(int v) :			value(v)		{}};class child : public base {	public:		void print() const {			std::cout << "child: " << value << std::endl;		}		child(int v) :			base(v)		{}};int main() {	base   b(1);	child  c(2);	base  &ref_c = c;	b.print();	c.print();	ref_c.print();	/* Output:	 * base 1	 * child 2	 * base 2	 */}

// C++ with virtual#include <iostream>class base {	protected:		int value;	public:		virtual void print() const {			std::cout << "base: " << value << std::endl;		}		base(int v) :			value(v)		{}};class child : public base {	public:		virtual void print() const {			std::cout << "child: " << value << std::endl;		}		child(int v) :			base(v)		{}};int main() {	base   b(1);	child  c(2);	base  &ref_c = c;	b.print();	c.print();	ref_c.print();	/* Output:	 * base 1	 * child 2	 * child 2	 */}

Okay you know how that all works already. Now to show you why they differ by going lower-level which I'm going to do in C because I can. Well, actually, because it seems more apt as you have no built-in virtual to rely upon. When you do not use virtual, it ends up being kind-of, sort-of implemented like this:
/* C with no virtual */#include <stdio.h>struct base {	int value;};void base_ctor(struct base *t, int v) {	t->value = v;}void base_print(const struct base *t) {	printf("base: %d\n", t->value);}struct child {	struct base parent;};void child_ctor(struct child *t, int v) {	base_ctor(&t->parent, v);}void child_print(const struct child *t) {	printf("child: %d\n", t->parent.value);}int main() {	struct base   b;	struct child  c;	struct base  *ref_c = &c.parent;	base_ctor(&b, 1);	child_ctor(&c, 2);	base_print(&b);	child_print(&c);	base_print(ref_c);	/* Output:	 * base 1	 * child 2	 * base 2	 */}

Relatively simple. So, let's make it weird by pretending we're a C++ compiler in C:
/* C with virtual */#include <stdio.h>/* base */void base_print_impl(const void *);struct base_vtable_type {	void (*print)(const void *);} base_vtable_impl = {	&base_print_impl};struct base {	struct base_vtable_type *vtable;	int value;};void base_print_impl(const void *t_void) {	struct base *t = (struct base *) t_void;	printf("base: %d\n", t->value);}void base_ctor_base(struct base *t, int v) {	t->value = v;}void base_ctor(struct base *t, int v) {	t->vtable = &base_vtable_impl;	base_ctor_base(t, v);}void base_print(struct base *t) {	(*t->vtable->print)(t);}/* child */void child_print_impl(const void *);struct base_vtable_type child_vtable_impl = {	&child_print_impl};struct child {	struct base parent;};void child_print_impl(const void *t_void) {	struct child *t = (struct child *) t_void;	printf("child: %d\n", t->parent.value);}void child_ctor_base(struct child *t, int v) {	base_ctor_base(&t->parent, v);}void child_ctor(struct child *t, int v) {	t->parent.vtable = &child_vtable_impl;	child_ctor_base(t, v);}int main() {	struct base   b;	struct child  c;	struct base  *ref_c = &c.parent;	base_ctor(&b, 1);	child_ctor(&c, 2);	base_print(&b);	base_print(&c.parent);	base_print(ref_c);	/* Output:	 * base 1	 * child 2	 * child 2	 */}

Makes you feel a little bit sorry and thankful for your C++ compiler when you use virtual, no? In a system like what you're proposing the compiler would have to implement every function as virtual (look at Java for what that actually means) as there's no way to guess whether a class will be derived from or not upon its declaration.

(That took a while to type up, so I was beat to it ;).)
i just compiled a test app that has a simple base class and a simple class deriving from it.

base class: Employee
derived class: Cashier

Employee has a function:
public virtual void draw()
{
// draw.
}

Cashier has a function:
public virtual void draw()
{
// draw.
}

this DOES NOT cause a compile-time error.
when i invoke the draw function from an instance of Cashier, then Cashier's draw is invoked. i can also invoke Employee's draw by simply making an explicity call to it (by type casting).

this makes me believe that the base virtual function draw() is indeed "hidden".
Yup. C# requires override. It tosses a Warning, which you can (and should) configure to actually be an Error. C++ it'll not be hidden.

That code is wholy broken:
Employee Someone=new Cashier();Someone.Draw();// Employee.Draw() is run. Oops...

This topic is closed to new replies.

Advertisement