Tom Gunn 1,164 Practically a Master Poster

This is a pretty rough hierarchy. There is still one ambiguity because you do not virtually inherit B in D and F, so there will be two instances of B in H. If you fix that, then H will have one copy of each class all the way up to A.

Tom Gunn 1,164 Practically a Master Poster

You do not need to pair isupper and tolower. If the character passed to tolower does not have a lower case equivalent then the same character will be returned:

#include <iostream>
#include <string>
#include <cctype>

int main()
{
    using namespace std;

    string input;

    cout << "Enter a word: ";
    cin >> input;

    for (int i = 0; i < input.size(); i++)
    {
        input[i] = tolower(input[i]);
    }

    cout << input << '\n';
}

Why does the word need to be added to an array? Can it not be changed in place like my example? If not, a string would be much better than an array because with an array you have to figure out the size and do other stuff that is not necessary in C++.

Tom Gunn 1,164 Practically a Master Poster

The error is very generic. It could be a problem in your code. It could be a problem with your compatibility settings in either the compiler or the exe. Or it could just be one of those random errors that creep up when you use ancient software with modern operating systems. M$ tries to be compatible with old software and hardware, but that does not mean it always succeeds.

Let me guess: you are running Windows XP, Vista, or 7, right?

Make sure your code is right by using one of the graphics samples that come with the compiler. It should be in the documentation. If you are sure the code is right, make sure that you are running Turbo C++ and the compiled exe under the right compatibility settings by right clicking the exe, going to properties and checking every check box in the compatibility tab.

If none of that works, you can downgrade your OS. Virtual machines make it easy to run Windows 95 or something like that so your OS fits the programs you are writing.

You should not even need to be here asking about this. If your teacher forces you to use a compiler that does not fit the OS, it is his responsibility to help you get it running. You are paying for the class, so get your money's worth.

Tom Gunn 1,164 Practically a Master Poster

There is also cout.width(...).

cout.width() is not sticky. You need to call it for every object that gets printed, so code like this will only right justify the first string:

cout.width(30);
cout << "right justified\n"
     << "left justified\n";

To justify both strings, you need to split the output into two statements because cout.width() does not return a reference to the stream and cannot be chained:

cout.width(30);
cout << "right justified\n";
cout.width(30);
cout << "left justified\n";

The manipulator lets you do it all in one statement:

cout << setw(30) << "right justified\n"
     << setw(30) << "right justified\n";

So while cout.width() exists, I do not think there is much reason to use it unless you really hate including <iomanip> for the parameterized manipulators. ;)

necrolin commented: Agreed +1
Tom Gunn 1,164 Practically a Master Poster

The errors are telling you that Turbo C++ is almost 20 years old and it is time to move to a newer compiler that does not need a DOS emulator to run your programs in 16 bit mode. ;) Really, there is no point in learning to do graphics with Turbo C++ because you will not use it in the real world anymore. The only reason it still works is because M$ is hell bent on having backward compatibility to the beginning of time.

Tom Gunn 1,164 Practically a Master Poster

The fstream class is declared in the <fstream> header, not <iostream> or <stdio.h>. And a function needs to be declared before it can be used. You can declare it by moving the definition before main like my last example, or by adding a prototype before main and leaving the definition in the same place as your code:

#include <iostream>
#include <fstream>

using namespace std;

void GetNext(fstream& is);

int main()
{
    fstream file("test.txt", ios::in | ios::out | ios::binary);

    GetNext(file);
    GetNext(file);
    GetNext(file);
}

void GetNext(fstream& is)
{
    cout << (char)is.get() << '\n';
}
Tom Gunn 1,164 Practically a Master Poster

Array variables are converted to pointers for value operations. You do not need to do anything special if you already have a pointer. The syntax is basically the same as with an array variable except you have more options with pointer arithmetic:

#include <stdio.h>

int main()
{
    int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int const* p = &a[0];
    size_t const sz = sizeof a / sizeof *a;
    size_t x;

    /* loop with indexes on the array */
    for (x = 0; x < sz; ++x) printf("%d%s", a[x], x < sz - 1 ? " " : "\n");

    /* loop with indexes on the pointer */
    for (x = 0; x < sz; ++x) printf("%d%s", p[x], x < sz - 1 ? " " : "\n");

    /* loop with pointer arithmetic */
    for (; p != &a[sz]; ++p) printf("%d%s", *p, p + 1 != &a[sz] ? " " : "\n");

    return 0;
}
Tom Gunn 1,164 Practically a Master Poster

Daniweb probably has to comply with certain information archival laws. If any threads are physically deleted from the database, that could be grounds for heavy fines or even complete shutdown of the site. Or the information could be used to prosecute spammers, but physical deletion makes coming up with a case harder.

I mean no offense, but I have been part of the administration for a forum before. Most of the complaints and suggestions I got were too narrow minded. They focused on one aspect of the big picture and did not consider all of the other factors. Big forums like Daniweb do not get popular by having arbitrary rules and processes. We should be confident that there is a good reason for everything, even if it does not make sense to those of us without inside information. :)

lllllIllIlllI commented: Hahah nice. i was going to say that. but a lot less nice. I get sick of constant b**tching :P +4
Tom Gunn 1,164 Practically a Master Poster

So I have two functions, both which have the same name, but based on the TYPE of the parameter passed do two separate things

You described function overloading, not overriding. Overriding is replacing the implementation of a method with a specialized implementation in a derived class.

Tom Gunn 1,164 Practically a Master Poster

I still do not see what is wrong with giving somebody good reputation points for a good question. Why is a whole new system needed for that?

Tom Gunn 1,164 Practically a Master Poster

Why? How is that different from the reputation points you get when someone randomly agrees or disagrees with you?

Tom Gunn 1,164 Practically a Master Poster

No need for a case statement.

If you add a bunch of restrictions to make table lookup work. ;) Both ways are valid, and it depends on the situation. It is not uncommon for the values in an enumeration to span a large range and be very sparse within that range. Table lookups are not as attractive in that case.

Anyway, thank you for giving another example of explicitly mapping strings to enumerations. I assumed that one simple example would be enough to get the creative juices flowing, but I have been wrong before.

Tom Gunn 1,164 Practically a Master Poster

What do you mean by a points system? The whole idea is pretty vague.

Tom Gunn 1,164 Practically a Master Poster

Try it and see. Like I mentioned already, testing this stuff is easy easy. :)

Tom Gunn 1,164 Practically a Master Poster

Not without doing some kind of explicit mapping:

#include <iostream>
#include <string>

enum Color {Red, Blue, Green};

static std::string ToString(Color color)
{
    switch (color)
    {
        case Red:   return "Red";
        case Blue:  return "Blue";
        case Green: return "Green";
        default: return "";
    }
}

int main()
{
    Color color = Red;

    std::cout << ToString(color) << '\n';
}
Tom Gunn 1,164 Practically a Master Poster

Plus,

Doing

file.get(header,4);
file.seekg(-4,ios::cur);

gets the get pointer back in its original point? I'm kinda confused with this :S

Stand up and take 4 steps forward, then take 4 steps back. Now you are right where you started. This is the same principle expressed as C++ code. :)

Tom Gunn 1,164 Practically a Master Poster

The get and set pointers will not revert to their value before the function was called when the function returns because the stream object needs to be passed as a reference. Try passing by value and you will get an error. ;)

The behavior is easy to test:

#include <iostream>

void GetNext(std::istream& is)
{
    std::cout << (char)is.get() << '\n';
}

int main()
{
    GetNext(std::cin);
    GetNext(std::cin);
    GetNext(std::cin);
}
Tom Gunn 1,164 Practically a Master Poster

You can use any sorting algorithm with the right comparison:

#include <stdio.h>
#include <stdlib.h>

int compare(void* const a, void* const b)
{
    int const ia = *(int const*)a;
    int const ib = *(int const*)b;

    return (abs(ia) % 2) - (abs(ib) % 2);
}

int main()
{
    int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    size_t const sz = sizeof a / sizeof *a;
    size_t x;

    qsort(a, sz, sizeof *a, compare);

    for (x = 0; x < sz; ++x) printf("%d%s", a[x], x < sz - 1 ? " " : "\n");

    return 0;
}

It does not matter that I used qsort. The comparison function is the part that matters for this example. Another way to do it is walk down the array checking for evens, and if the value is not even, move it to the end. The only hard part to that method is filling in the hold left by the vacated value:

/* fill in the hold at &a[x] */
memmove(&a[x], &a[x+1], (SIZE - x) * sizeof *a);
/* now a[SIZE-1] is open for */

You can do the same thing with a loop by shifting every value after the vacated value one spot to the left:

/* fill in the hold at &a[x] */
for (y = x; y < SIZE - 1; ++y) a[y] = a[y+1];
/* now a[SIZE-1] is open for */
Tom Gunn 1,164 Practically a Master Poster

The flip side is that items are pushed right to left.

In the compilers I have worked with, that is true. But the compiler is free to do it the most convenient way for internal handling, which could just as easily be any other order. That is why even if the compiler does something sensible for undefined behavior, you still cannot explain the output consistently.

Tom Gunn 1,164 Practically a Master Poster
char info;

info is not a string, it is a single char. That explains why you only get the first character of the surname. Change info to an array of 255 like the surname array in the user structure, or change the type to an object of the user structure:

typedef struct{
    char dummy[256];
    char surname[225];
    char name[256];
    int ID;
    char house[256];
    char street[256];
    char town[256];
    char county[256];
    char area[256];
    char country[256];
    char postcode[256];
    int phone;
    char email[256];
    char boattype[256];
    char boatname[256];
}user;

struct tree {
  user info;
  struct tree *left;
  struct tree *right;
};

This should give you a good start for making the code do what you want. :)

Tom Gunn 1,164 Practically a Master Poster

There probably will not be a definition of a like that. If the variable is local, it will probably be defined as an offset on the stack. Look for changes to esp and ebp:

void Function()
{
    int a;

Might look like this for whatever flavor of assembly is output. I do not know gas well enough to give you an example of it ;):

; create a stack frame with a local int variable
push ebp
mov ebp, esp
sub esp, 4
Tom Gunn 1,164 Practically a Master Poster

Two instances of undefined behavior, three if your compiler does not support void main.

  1. The i variable is uninitialized. Anything could be in that memory space, and you are not allowed even to read it.
  2. A sequence point is only guaranteed after all of the function arguments are evaluated. Modifying a variable more than once between sequence points is undefined.

In other words, anything could happen and there is no way to explain your output that is consistent.

Tom Gunn 1,164 Practically a Master Poster

Can you explain your design? A Frame is an Mp3, but an Mp3 has a list of Frames, so an Mp3 has a list of Mp3s? Why does Frame need to inherit from Mp3? That does not make sense to me, but I admit that I only know enough about Mp3s to play them. ;)

The error you are getting means that Frame is not defined when Mp3 is defined, but Mp3 uses Frame in the class definition. Problems like that can usually be solved by forward declaring the class being used:

class Frame;

class Mp3 {
	list<Frame> frames;
	fstream myfile;
	unsigned long length;
public:
	char * name;

	Mp3(){}
	Mp3(const char * n);
	const char * Getname();
	unsigned long getLen(const char * path);
	bool isOk();
	bool getFirstFrame(Frame& f);
	list<Frame> getFrames(const char * path);
	int visitFrames(FrameVistior fv);
	void WriteFrame(Frame fr, FILE * dst);
};

But I am not sure that fixing the actual error makes as much sense as solving what might be a design problem.

Tom Gunn 1,164 Practically a Master Poster

I never liked the term "Self Referential Structure" because it implies that objects of the structure always refer to themselves. A self referential structure is better called a recursive structure. If the pointer is not NULL, it points to another instance of the structure. The null pointer is a base case for recursion. You can print a list recursively as an example of the relationship between self referential structures and recursion:

#include <stdio.h>

typedef struct Node
{
    int item;
    struct Node *next;
} Node;

void RecursiveTraverse(Node const* head);

int main()
{
    /* manually define a list for the example */
    Node listBase[] = 
    {
        {0, NULL},
        {1, NULL},
        {2, NULL},
        {3, NULL},
    };
    size_t const sz = sizeof listBase / sizeof *listBase;

    {
        /* set all links but the last node */
        size_t x;
        for (x = 0; x < sz - 1; ++x)
        {
            listBase[x].next = &listBase[x + 1];
        }
    }

    /* run example of recursive structure */
    RecursiveTraverse(&listBase[0]);

    return 0;
}

void RecursiveTraverse(Node const* head)
{
    /* null pointer is the base case */
    if (!head) return;

    printf("%d\n", head->item);
    RecursiveTraverse(head->next);
}

If the pointer points to the same object, it is a circular reference that represents infinite recursion because there is no base case:

Node circular = {0, NULL};

circular.next = &circular; /* now a circular reference */

Circular references are usually a bad thing unless you have some logic in place to resolve it into a base case. Ring buffers are a good example of …

Tom Gunn 1,164 Practically a Master Poster

since generating 1 from (float)rand()/RAND_MAX has low odds

I am not good at taking people's word. ;) Why does (float)rand()/RAND_MAX have low odds of generating 1 and how does it justify adding .1 in all cases?

Tom Gunn 1,164 Practically a Master Poster

If you want to manually refactor, I like to start with a simple function call and cut the code into a new function with that name:

void BottomTurn()
{
    a = front[6];
    b = front[7];
    c = front[8];

    front[6] = left[0];
    front[7] = left[3];
    front[8] = left[6];

    left[0] = back[2];
    left[3] = back[1];
    left[6] = back[0];

    back[0] = right[2];
    back[1] = right[5];
    back[2] = right[8];

    right[2] = c;
    right[5] = b;
    right[8] = a;

    aa = bottom[0];
    bb = bottom[1];

    bottom[0] = bottom[6];
    bottom[6] = bottom[8];
    bottom[8] = bottom[2];
    bottom[2] = aa;
    bottom[1] = bottom[3];
    bottom[3] = bottom[7];
    bottom[7] = bottom[5];
    bottom[5] = bb;
    bottom[4] = bottom[4];
}

The code almost never compiles like that because there are dependencies that need to be passed to the function, like the front, left, back, right, and bottom arrays. Variables that are only used for that operation become local, like a, b, c, aa, and bb:

template <typename T>
void BottomTurn(T front[], T left[], T back[], T right[], T bottom[])
{
    T a = front[6];
    T b = front[7];
    T c = front[8];

    front[6] = left[0];
    front[7] = left[3];
    front[8] = left[6];

    left[0] = back[2];
    left[3] = back[1];
    left[6] = back[0];

    back[0] = right[2];
    back[1] = right[5];
    back[2] = right[8];

    right[2] = c;
    right[5] = b;
    right[8] = a;

    T aa = bottom[0];
    T bb = bottom[1];

    bottom[0] = bottom[6];
    bottom[6] = bottom[8];
    bottom[8] = bottom[2];
    bottom[2] = aa;
    bottom[1] = bottom[3];
    bottom[3] = bottom[7];
    bottom[7] = bottom[5];
    bottom[5] = bb; …
Tom Gunn 1,164 Practically a Master Poster

Did not know you could override properties!(This is true is it?)

It is true. Properties can also be part of an interface.

Tom Gunn 1,164 Practically a Master Poster

Or loop through the string and check if any char is between the acsii value's of '0' and '9'.

Or just compare against '0' and '9' because the values have to be contiguous and using the underlying values means your code is tied to ASCII and any other character sets that correspond directly to ASCII for the decimal digits:

int isDigit = (c >= '0' && c <= '9');

Do not write unportable code if the portable version is easier to read, write, and maintain. :)

Tom Gunn 1,164 Practically a Master Poster

I think a property is better because the method has extra syntax that is not needed. But they both do the same thing and are close enough that you should pick the one you like better.

Suppose I want to derive from a Square class, call it a SuperSquare class, what is the best option?

Properties can be virtual. Adding inheritance and polymorphism to the question does not change the answer. :)

Tom Gunn 1,164 Practically a Master Poster

Your function generates a random number in the range of [0..RAND_MAX), not in the range of [1..5). The easiest trick for fixing the range is the remainder operator:

i = rand() % 4 + 1;

The remainder of division from RAND_MAX by 4 will always result in a value in the range of [0..4), or 0, 1, 2, and 3. Adding 1 to that shifts the range to [1..5). The easiest trick might not be the best trick if rand() does not have good randomization of low order bits.

The random() function should probably do something like this, with whatever random trick you want:

int random(int lb, int ub)
{
    return std::rand() % (ub - lb) + lb;
}

Then in the calling function, call random() in a loop 8 times:

for (int x = 0; x < 8; ++x) cout << random(1, 5) << '\n';
Tom Gunn 1,164 Practically a Master Poster

Plenty of questions, plenty of weak programmers, or both ?

Yes. :D

Tom Gunn 1,164 Practically a Master Poster

It's not because you quickly want to test out some code that you may use bad coding styles.
Bad coding styles are considered: bad, so you don't have to use them, under any circumstances.

That rationalization might work when you preach to the choir, but everyone else needs a better reason. :) The better reason is that you are not just a tutor but a role model too. If you use bad coding practices then those practices will be emulated. If you use good coding practices then *those* practices will be emulated. Set a good example because you do not need to create more questions by training weak programmers. There are already plenty. ;)

tux4life commented: The better example is in place here :) +18
Tom Gunn 1,164 Practically a Master Poster

Most of us do not write hello world programs all day. ;) The more you learn about C, the more you can imagine C's potential.

Tom Gunn 1,164 Practically a Master Poster

When the compiler complains about dependent names, it means you need to use the typename keyword to fix an ambiguity:

template<typename T> class List
{
public:
	class A
	{
	};

	A begin() const;
};

template<typename T>
typename List<T>::A List<T>::begin() const
{
	return A();
}
Tom Gunn 1,164 Practically a Master Poster

fscanf can read 2 digit hex numbers:

#include <stdio.h>

int main()
{
    unsigned char binary[16] = {0};
    unsigned int hex;
    int x, y;

    for (x = 0; x < 16 && fscanf(stdin, "%2x", &hex) == 1; ++x)
        binary[x] = (unsigned char)hex;

    for (y = 0; y < x; ++y) printf("%x\n", binary[y]);

    return 0;
}
Tom Gunn 1,164 Practically a Master Poster

You have textBox1 and textBox2. From the names I guess you dragged and dropped using the visual designer. What that does is generate a partial class for you with all of the dirty work of creating and placing controls. You can look at that code by finding the <filename>.Designer.cs file in your project that corresponds to the <filename>.cs file.

All of that extra fluff takes space and adds complexity, so I did it manually by creating controls and putting them directly into the form class without using a partial class. My _sentence and _vowelCount are the same as your textBox1 and textBox2.

Tom Gunn 1,164 Practically a Master Poster

Have you tried opening with FILE_APPEND_DATA instead of GENERIC_WRITE?

Tom Gunn 1,164 Practically a Master Poster

infile is local to the if statement and the file gets closed when the if statement ends. Inside the loop, the file is closed, the object pointed to by the pointer is gone and you are not allowed to dereference the pointer anymore. Even if you have a pointer to an object, you need to make sure that the object still exists as long as the pointer is being used:

#include <iostream>
#include <fstream>
using namespace std;

int main(int argc, char *argv[]) {
    istream *fin = &cin;
    ifstream infile;

    if(argc > 1) 
    {
        infile.open(argv[1], ios::in);
        if (!infile) return 1;
        fin = &infile;
    }

    while (1)
    {
        char c;
        fin->get(c);

        if (! *fin) break;
        cout.put(c);
    }
    return 0;
}
Tom Gunn 1,164 Practically a Master Poster

Static members of a class still need to be defined outside of the class, and in a file that will not be included multiple times like a header:

// globals.h
#include <string>

struct car
{
	std::string type;
	std::string brand;
	std::string manufacturer;
	std::string engine;
	int cylinders;
	std::string tyres;
};

public class globals
{
public: 
	static car test[100];
};
// globals.cpp
#include "globals.h"

car globals::test[100];
Tom Gunn 1,164 Practically a Master Poster

Can someone suggest me some way to increase my interest in C by giving examples of some marvellous things that can be done in C and CPP.

If you need random anonymous people online to help you get interested, you are not interested enough. Maybe another language where you can get started faster will spark more interest? Python was already mentioned, and any of the .NET languages will be friendlier than C.

I reccomend starting with QBASIC (almost everyone is familar with basic, so it can be used to communicate idea's to other programmers)

QBASIC? I am getting flashbacks of the early 90s. It's Hammer Time! QBASIC went out of style along with slap bracelets, Furby, and fanny packs. I think you will have a hard time finding barely anyone, much less 'almost everyone' who can read and understand classic BASIC. If there is no V to the B you will only get blank stares. ;)

Tom Gunn 1,164 Practically a Master Poster

For a good block cipher the responsible thing to do is change the constraint to expect cipher text length instead of plain text length or change the limit of the plain text to be short enough to meet the current database constraint. If you use a block cipher it is easy to figure out the size of the cipher text because it will be a multiple of the block size for the algorithm.

For example, if you want to use Blowfish and the plain text limit is 15 characters, that is 30 bytes for C#'s char type and the next block multiple is 32 bytes because Blowfish uses a block size of 8 bytes. Then you need to add the overhead of a text representation like base 64. Base 64 adds 4 characters to the string for every 3 bytes and does some padding for lengths not divisible by 3. To hold the base 64 cipher text of 15 two byte characters the database constraint should grow from 15 characters to 44 characters.

Growing the database constraint is better than shrinking the application constraint because if you do the math, the plain text length would have to be very small for 15 base 64 characters.

If you really want the cipher text to have the same length as the plain text, look into stream cipher algorithms. AES can be run in a mode called CFB that turns it into a stream cipher. Or you can use any …

Tom Gunn 1,164 Practically a Master Poster

I suppose I should treat like a full commercial application.

I've gone with XOR encryption.

I just died a little on the inside. :(

Tom Gunn 1,164 Practically a Master Poster

Do you have some tricks for narrowing down a search on MSDN?

I use google to search MSDN because the site is slow. ;) Usually I search for class names or names that might be class names and google gives me what I want. But if you want a list of all the classes and functions, you kind of have to browse MSDN instead of search.

Tom Gunn 1,164 Practically a Master Poster

operator>> is overloaded and will pick the right function for the type you want if there is an overload of that type. You can change int to float or double and the code will still work.

Tom Gunn 1,164 Practically a Master Poster

You can do it, but I think a better way is to have a Clone() method in the Animal class that the child classes override. Then all you need to do is this:

foreach (Animal d in AnimalBufferList)
{
    Add(d.Clone());
}
sknake commented: Thats what I would do +6
Tom Gunn 1,164 Practically a Master Poster

Can anyone recommend another book or website that lists all the C++/CLI functions and classes? Perhaps one that just is just a list with maybe one short example each?

You just described MSDN.

Tom Gunn 1,164 Practically a Master Poster

a friend of mine had a problem that encrypting the same string twice gave different results so I was trying to avoid it.

How did your friend do the encryption? Handmade encryption code can do all kind of funny things. The code I gave can handle encrypting multiple times as long as you decrypt the same number of times.

Tom Gunn 1,164 Practically a Master Poster

My name is Tommy, and I am a computer consultant/programmer in the USA. C# and C++ are my primary weapons, and most of my work these days is .NET based for data capture, processing, storage, and retrieval.

My hobbies are gaming, music, ultimate frisbee, and cooking. My personality type is ESFP.

Tom Gunn 1,164 Practically a Master Poster

But you will probably agree with me there are plenty of companies out there who still store their sensitive info in just plain text format.

Sure, but that is no excuse for writing insecure code. :) There are also plenty of stories about companies being fined, sued, and burned big time for having sensitive info stolen. I do not want to be the programmer that caused that. ;)

Tom Gunn 1,164 Practically a Master Poster

You are kind of missing the point of the KeyDown event. It gets fired when a new character is added to the text box, and only that new character needs to be checked for a vowel. With a label holding the running total and being updated each time the event is fired, there is no need for a button. Here is a simple form that shows what I mean:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace Daniweb
{
	class MainForm: Form
	{
		#region Form Controls
		private TextBox _sentence;
		private Label _vowelCount;
		#endregion

		private int _vowels = 0;

		public MainForm()
		{
			SuspendLayout();

			_vowelCount = new Label();
			_vowelCount.Dock = DockStyle.Top;
			UpdateCount();
			Controls.Add(_vowelCount);

			_sentence = new TextBox();
			_sentence.Dock = DockStyle.Top;
			_sentence.KeyDown += _sentence_KeyDown;
			Controls.Add(_sentence);

			ResumeLayout();
		}

		void _sentence_KeyDown(object sender, KeyEventArgs e)
		{
			if (IsVowel(e.KeyData)) ++_vowels;
			else
			{
				// handle backspacing and deleting if you want
			}

			UpdateCount();
		}

		private bool IsVowel(Keys key)
		{
			return key == Keys.A ||
			       key == Keys.E ||
			       key == Keys.I ||
			       key == Keys.O ||
			       key == Keys.U;
		}

		private void UpdateCount()
		{
			_vowelCount.Text = "There are " + _vowels + " vowels.";
		}
	}
}