For your first question, you have a choice as to whether you filter moves that expose the king at generation time or you do so when you actually try to make the move in the alpha-beta loop. The latter is simpler and it's what I have always done. People refer to this as "pseudo-legal" move generation. It might be worth having a specialized function that generates check evasions, because is you are in check most of the pseudo-legal moves are likely to be illegal, and legal move generation is going to be faster. You should probably not do this at first, and leave it as a possible optimization for later on.
As to how to improve move generation, the main idea is to stagger move generation, so you only generate the moves you need. This is closely related to move ordering heuristics, which are critical to be able to reach high depths.
The chess programming wiki (which you should bookmark) has a good description of a typical move ordering in a chess program. If you use something like that, you should have a function to verify if a move is valid from the current position, so you can try the hash move before generating any moves. You then generate captures, sort them (probably in MVV-LVA order) and try them out, evaluating the ones with positive SEE and saving for later the ones with negative or zero SEE (you'll have to test lots of choices here to see what works best for your engine). Then again you can use the function that verifies if a move is valid from the current position, to try out the killer moves without generating any other moves. Then you generate non-captures.
Organizing the code to do all of this can be messy. I recommend creating a MoveGenerator object that keeps track of where you stand in the process, what lists of moves you still have to go through, etc. The interface to this object consists of a method get_next_move(), so code that uses this can be kept very clean.
Also very important is the move generation in the quiescence search, because that's likely to be where your engine spends most of its time. For that, you want to use the capture generator, the check-evasion generator and perhaps a specialized generator for non-capturing checks, if you are going to consider those moves in your quiescence search (again you need to test lots of options to see what works for your engine).
I have mentioned a couple of places where you will need to test different options. That is the main mode of development of an engine once you manage to put something together that obeys the rules and can talk UCI. So make sure you find a good mechanism to test one version of your program against another or against some reference opponents.
That should keep you busy for a while.