For the past few weeks, I have been thinking of a way to make rather unrelated objects to communicate with each other. While I have been able to find several ways to make it work, they all seem to be somewhat unelegant or overly complicated.
One of the working solutions was to create some sort of a super-class which serves the role of a "mediator", checking the state of its member objects and responding accordingly.
struct SuperClass {
Car playerCar;
Police police;
void update() { if(playerCar.exceedsSpeed()) { police.call(); } }
};
While this could work well in simple programs, this design tends to get messy with complexity. As more different object types are created, more super-classes or mid-super-classes are required. Eventually you end up with a strict hierarchy of classes which are extremely difficult to maintain.
Another solution is to pass the reference of an object as an argument.
struct Car {
Police* pPolice;
Car(Police* obj) { pPolice = obj; }
};
Police police;
Car playerCar(&police);
Every call to the Police::call() could be made through pPolice, but there are two issues with this solution.
First, the Car struct now doesn't make any sense. If every car has various characteristics such as color, manufacturer, maxSpeed, and so on, why the hell does every car have a police? Since objects are structures containing variables specific to the type of that object, including a completely unrelated variable among those variables breaks the aesthetics of OOP.
Second, in case Car needs to call methods of more objects than just police.call(), I would now need to pass more references as arguments.
Car playerCar(&police, &anotherObj, &yetAnotherObj...)
... And this is just silly.
Instead, I would like to be able to call the appropriate methods locally.
void Car::accelerate() {
speed++;
if (speed > maxSpeed) { Mediator::callPolice(); }
};
How do I do that? Is that possible? Is that even the correct way?
Seems like you normally have two cases.
One is that the police catch somebody speeding. They don't call the police to report themselves--rather, an individual police officer has some area in which he can observe, and if a car exceeds the limit while traveling through that area (and the police officer isn't otherwise occupied), he's caught and gets issued a ticket (or whatever).
The second, is that the driver of the car decides to call the police (e.g., there's been an accident). In this case, there is a "mediator"-like class. Specifically, there's a class to represent the phone system, and the driver (or another driver who sees) uses their phone to call the police. If nobody with a phone sees the incident, then the police might just not get called--or there might at least be a delay while the driver finds somebody with a phone.
Create a public static method "callPolice" inside your "Police" struct. Then you could call it from a method inside Car struct. This would work for you if body of "callPolice" method does not need to execute on an instance of a specific Police object.
Related
Given the following class:
class ToggleOutput {
public:
uint32_t count;
ToggleOutput(PARAMETERS) //I've just removed stuff to reduce the code
{
// The code when setting things up
}
void Update() // public method to toggle a state
{
// this method will check if a time period has elapsed
// if the time period has elapsed, toggle an output
// Each time the output is toggled on then count gets incremented
count += 1;
}
};
Later on in the code, several instances of ToggleOutput get created
ToggleOutput outPut_1(PARAMETERS); // Again, PARAMETERS are just the stuff
ToggleOutput outPut_2(PARAMETERS); // I've cut out for brevity.
ToggleOutput outPut_3(PARAMETERS);
ToggleOutput outPut_4(PARAMETERS);
during execution, I want to do stuff, based on the value of the class member variable, count. eg
if (outPut_1.count >= SOMEVALUE)
do_some_stuff();
I have been told that this is not acceptable. To follow the 'tenets of OOP', class methods should be impletmented to interact with class variables from outside of the class, eg the above code would need to become
if (outPut1.getCount() >= SOMEVALUE)
and the class variable count would need to be made private.
Is this true? Or is it acceptable to allow direct access to class variables if required
Or is it acceptable to allow direct access to class variables if required
A lot of research into good software engineering and programmer productivity indicates that it's typically good to hide the details of how something is implemented. If person A writes a class, then s/he has certain assumptions about how the class should work. If person B wants to use the class, then s/he often has different assumptions about how the class should work (especially if person A did not document the code well, or even at all, as is the case all too often). Then person B is likely to misuse the data in the class, which can break how the class methods work, and lead to errors that are difficult to debug, at least for person B.
In addition, by hiding the details of the class implementation, person A has the freedom to complete rework the implementation, perhaps removing the variable count and replacing it with something else. This can occur because person A figures out a better way to implement count, or because count was in there only as a debugging tool and is not necessary to the actual working of ToggleOutput, etc.
Programmers don't write code only for themselves. In general, they write code for other people, that will be maintained for other people. "Other people" includes you five years from now, when you look at how you implemented something and ask yourself, What on earth was I thinking? By keeping the details of the implementation hidden (including data) you have the freedom to change that, and client classes/software don't need to worry about it as long as the interface remains the same.
Basically, member access is a rule you impose to the developers.
It's something you put in place to prevent yourself or another developer using your class from modifying properties that are supposed to be managed only by the class itself and nobody else.
It has nothing to do with security (well, not necessarily anyway), it's more a matter of semantics. If it's not supposed to be modified externally, it should be private.
And why should you care? Well, it helps you keep your code coherent and organized, which is specially important if you are working with a development team or with code that you intent to distribute.
And if you have to document your class, you only have to do so for stuff that is public, as far as the class user is concerned nothing else matters.
I'm wondering if there is some kind of logical programming pattern or structure that I should be using if sometimes during runtime a component should be used and other times not. The obvious simple solution is to just use if-else statements everywhere. I'm trying to avoid littering my code with if-else statements since once the component is toggled on, it will more than likely be on for a while and I wonder if its worth it to recheck if the same component is active all over the place when the answer will most likely not have changed between checks.
Thanks
A brief example of what I'm trying to avoid
class MainClass
{
public:
// constructors, destructors, etc
private:
ComponentClass m_TogglableComponent;
}
// somewhere else in the codebase
if (m_TogglableComponent.IsActive())
{
// do stuff
}
// somewhere totally different in the codebase
if (m_TogglableComponent.IsActive())
{
// do some different stuff
}
Looks like you're headed towards a feature toggle. This is a common occurrence when there's a piece of functionality that you need to be able to toggle on or off at run time. The key piece of insight with this approach is to use polymorphism instead of if/else statements, leveraging object oriented practices.
Martin Fowler details an approach here, as well as his rationale: http://martinfowler.com/articles/feature-toggles.html
But for a quick answer, instead of having state in your ComponentClass that tells observers whether it's active or not, you'll want to make a base class, AbstractComponentClass, and two base classes ActiveComponentClass and InactiveComponentClass. Bear in mind that m_TogglableComponent is currently an automatic member, and you'll need to make it a pointer under this new setup.
AbstractComponentClass will define pure virtual methods that both need to implement. In ActiveComponentClass you will put your normal functionality, as if it were enabled. In InactiveComponentClass you do as little as possible, enough to make the component invisible as far as MainClass is concerned. Void functions will do nothing and functions return values will return neutral values.
The last step is creating an instance of one of these two classes. This is where you bring in dependency injection. In your constructor to MainClass, you'll take a pointer of type AbstractComponentClass. From there on it doesn't care if it's Active or Inactive, it just calls the virtual functions. Whoever owns or controls MainClass is the one that injects the kind that you want, either active or inactive, which could be read by configuration or however else your system decides when to toggle.
If you need to change the behaviour at run time, you'll also need a setter method that takes another AbstractComponentClass pointer and replaces the one from the constructor.
(Refer Update #1 for a concise version of the question.)
We have an (abstract) class named Games that has subclasses, say BasketBall and Hockey (and probably many more to come later).
Another class GameSchedule, must contain a collection GamesCollection of various Games objects. The issue is that we would, at times, like to iterate only through the BasketBall objects of GamesCollection and call functions that are specific to it (and not mentioned in the Games class).
That is, GameSchedule deals with a number of objects that broadly belong to Games class, in the sense that they do have common functions that are being accessed; at the same time, there is more granularity at which they are to be handled.
We would like to come up with a design that avoids unsafe downcasting, and is extensible in the sense that creating many subclasses under Games or any of its existing subclasses must not necessitate the addition of too much code to handle this requirement.
Examples:
A clumsy solution that I came up with, that doesn't do any downcasting at all, is to have dummy functions in the Game class for every subclass specific function that has to be called from GameSchedule. These dummy functions will have an overriding implementation in the appropriate subclasses which actually require its implementation.
We could explicitly maintain different containers for various subclasses of Games instead of a single container. But this would require a lot of extra code in GameSchedule, when the number of subclasses grow. Especially if we need to iterate through all the Games objects.
Is there a neat way of doing this?
Note: the code is written in C++
Update# 1: I realized that the question can be put in a much simpler way. Is it possible to have a container class for any object belonging to a hierarchy of classes? Moreover, this container class must have the ability to pick elements belonging to (or derive from) a particular class from the hierarchy and return an appropriate list.
In the context of the above problem, the container class must have functions like GetCricketGames, GetTestCricketGames, GetBaseballGame etc.,
This is exactly one of the problems that The "Tell, Don't Ask" principle was created for.
You're describing an object that holds onto references to other objects, and wants to ask them what type of object they are before telling them what they need to do. From the article linked above:
The problem is that, as the caller, you should not be making decisions based on the state of the called object that result in you then changing the state of the object. The logic you are implementing is probably the called object’s responsibility, not yours. For you to make decisions outside the object violates its encapsulation.
If you break the rules of encapsulation, you not only introduce the runtime risks incurred by rampant downcasts, but also make your system significantly less maintainable by making it easier for components to become tightly coupled.
Now that that's out there, let's look at how the "Tell, Don't Ask" could be applied to your design problem.
Let's go through your stated constraints (in no particular order):
GameSchedule needs to iterate over all games, performing general operations
GameSchedule needs to iterate over a subset of all games (e.g., Basketball), to perform type-specific operations
No downcasts
Must easily accommodate new Game subclasses
The first step to following the "Tell, Don't Ask" principle is identifying the actions that will take place in the system. This lets us take a step back and evaluate what the system should be doing, without getting bogged down into the details of how it should be doing it.
You made the following comment in #MarkB's answer:
If there's a TestCricket class inheriting from Cricket, and it has many specific attributes concerning the timings of the various innings of the match, and we would like to initialize the values of all TestCricket objects' timing attributes to some preset value, I need a loop that picks all TestCricket objects and calls some function like setInningTimings(int inning_index, Time_Object t)
In this case, the action is: "Initialize the inning timings of all TestCricket games to a preset value."
This is problematic, because the code that wants to perform this initialization is unable to differentiate between TestCricket games, and other games (e.g., Basketball). But maybe it doesn't need to...
Most games have some element of time: Basketball games have time-limited periods, while Baseball games have (basically) innings with basically unlimited time. Each type of game could have its own completely unique configuration. This is not something we want to offload onto a single class.
Instead of asking each game what type of Game it is, and then telling it how to initialize, consider how things would work if the GameSchedule simply told each Game object to initialize. This delegates the responsibility of the initialization to the subclass of Game - the class with literally the most knowledge of what type of game it is.
This can feel really weird at first, because the GameSchedule object is relinquishing control to another object. This is an example of the Hollywood Principle. It's a completely different way of solving problems than the approach most developers initially learn.
This approach deals with the constraints in the following ways:
GameSchedule can iterate over a list of Games without any problem
GameSchedule no longer needs to know the subtypes of its Games
No downcasting is necessary, because the subclasses themselves are handling the subclass-specific logic
When a new subclass is added, no logic needs to be changed anywhere - the subclass itself implements the necessary details (e.g., an InitializeTiming() method).
Edit: Here's an example, as a proof-of-concept.
struct Game
{
std::string m_name;
Game(std::string name)
: m_name(name)
{
}
virtual void Start() = 0;
virtual void InitializeTiming() = 0;
};
// A class to demonstrate a collaborating object
struct PeriodLengthProvider
{
int GetPeriodLength();
}
struct Basketball : Game
{
int m_period_length;
PeriodLengthProvider* m_period_length_provider;
Basketball(PeriodLengthProvider* period_length_provider)
: Game("Basketball")
, m_period_length_provider(period_length_provider)
{
}
void Start() override;
void InitializeTiming() override
{
m_period_length = m_time_provider->GetPeriodLength();
}
};
struct Baseball : Game
{
int m_number_of_innings;
Baseball() : Game("Baseball") { }
void Start() override;
void InitializeTiming() override
{
m_number_of_innings = 9;
}
}
struct GameSchedule
{
std::vector<Game*> m_games;
GameSchedule(std::vector<Game*> games)
: m_games(games)
{
}
void StartGames()
{
for(auto& game : m_games)
{
game->InitializeTiming();
game->Start();
}
}
};
You've already identified the first two options that came to my mind: Make the base class have the methods in question, or maintain separate containers for each game type.
The fact that you don't feel these are appropriate leads me to believe that the "abstract" interface you provide in the Game base class may be far too concrete. I suspect that what you need to do is step back and look at the base interface.
You haven't given any concrete example to help, so I'm going to make one up. Let's say your basketball class has a NextQuarter method and hockey has NextPeriod. Instead, add to the base class a NextGameSegment method, or something that abstracts away the game-specific details. All the game-specific implementation details should be hidden in the child class with only a game-general interface needed by the schedule class.
C# supports reflections and by using the "is" keyword or GetType() member function you could do these easily. If you are writing your code in unmanaged C++, I think the best way to do this is add a GetType() method in your base class (Games?). Which in its turn would return an enum, containing all the classes that derive from it (so you would have to create an enum too) for that. That way you can safely determine the type you are dealing with only through the base type. Below is an example:
enum class GameTypes { Game, Basketball, Football, Hockey };
class Game
{
public:
virtual GameTypes GetType() { return GameTypes::Game; }
}
class BasketBall : public Game
{
public:
GameTypes GetType() { return GameTypes::Basketball; }
}
and you do this for the remaining games (e.g. Football, Hockey). Then you keep a container of Game objects only. As you get the Game object, you call its GetType() method and effectively determine its type.
You're trying to have it all, and you can't do that. :) Either you need to do a downcast, or you'll need to utilize something like the visitor pattern that would then require you to do work every time you create a new implementation of Game. Or you can fundamentally redesign things to eliminate the need to pick the individual Basketballs out of a collection of Games.
And FWIW: downcasting may be ugly, but it's not unsafe as long as you use pointers and check for null:
for(Game* game : allGames)
{
Basketball* bball = dynamic_cast<Basketball*>(game);
if(bball != nullptr)
bball->SetupCourt();
}
I'd use the strategy pattern here.
Each game type has its own scheduling strategy which derives from the common strategy used by your game schedule class and decouples the dependency between the specific game and game schedule.
What does data abstraction refer to?
Please provide real life examples alongwith.
Abstraction has two parts:
Hide details that don't matter from a certain point of view
Identify details that do matter from a certain point of view and consider items to be of the the same class if they possess those details.
For example, if I am designing a program to deal with inventory, I would like to be able to find out how many items of a certain type the system has in stock. From the perspective of the interface system, I don't care if I am getting this information from a database, a csv file, a remote repository via a SOAP interface or punch cards. I just care that I can can say widget.get_items_in_stock() and know that it will return an integer.
If I later decide that I want to record that number in some other way, the person designing the interface doesn't need to know, care or worry about it as long as widget still has the get_items_in_stock() method. Like wise, the interface doesn't need to care if I subclass the widget class and add a get_square_root_of_items_in_stock() method. I can pass an instance of the new class to it just as well.
So in this example, we've hidden the details of how the data is acquired and decided that anything with a get_items_in_stock() method is an instance of the same class (or a subclass thereof) for certain purposes.
Data abstraction is any device that allows you to treat data as humans encounter it rather than as it is stored on machine.
At the lowest level, all primitive data types are abstractions -- as programmers, we don't usually have to deal with data at the bit level (which is how it is ultimately stored) but as integers, floating point numbers, characters, etc.
We then add layers onto that abstraction -- maybe two integers represents a Point, or we and enumerations to represent the months of the year, days of the week, etc.
With each abstraction layer, we move further from the machine and (hopefully) closer to human understanding of the data. This can extract a performance penalty -- it may not always be the case that points can be most efficiently represented by two integers. This is compensated for by the shorter development (and maintenance) time when abstractions are used.
The technique of creating new data type that is well suited to an application to be programmed is known as data abstraction.
Abstraction means providing only essential information to the outside world and hiding their background details..examp. In ur Computer u can see only monitor, keyboard nd mouse..u don't know anything about internal wiring this is abstraction.
Data abstraction seems to be explained as breaking data down as far as you can get it. food would be the abstraction of apple, orange, pizza. animal would be the abstraction of cat, cow, pig. A food object would be something like this pseudo code:
class food{
name;
calories;
weight;
public eat(name);
}
all foods have a name, calorie amount, and a weight. That's pretty abstract.
You could then make objects that inherit, which would be a bit less abstract.
class pizza inherits food{
toppings;
say_toppings();
}
pizza now has toppings, but it inherits name, calories, and weight from food.
basically abstraction has been explained as getting to the lowest level of each item and making classes that extend from them. It makes your code more reusable too... If you've bade your base class of food well enough, and included everything abstract about it anyone working in the food industry could use your class.
Abstraction is hiding the skeleton from the human body. The skin does a great way of containing it. (See how abstract I'm being there? Pun intended. I digress...)
If I have a water bottle, then I'm able to drink from it by opening the lid, twisting it until it pops off.
bool lid_open = false;
void open_water_bottle_by_twisting() { lid_open = true; }
But water bottles are containers. Containers hold liquids until they become open and they are able to be drunk from (assuming the liquid is drinkable).
class Container
{
bool lid_open = false;
protected:
Container() {}
void open_by_twisting()
{
lid_open = true;
}
public:
virtual ~Container();
};
class WaterBottle : public Container
{
WaterBottle() : Container() {}
public:
~WaterBottle();
};
However, not all containers are opened the same way. Some containers, such as the water bottle, have lids that can be twisted off. Others don't have lids, such as exercise bottles - those contain bendy straws that can be bent down for storage or up for drinking.
class Container
{
bool lid_open;
bool straw_open;
protected:
void TurnLid() { lid_open = true; }
void BendStraw() { straw_open = true; }
Container() : lid_open(false), straw_open(false){}
public:
virtual void open() = 0;
virtual ~Container();
};
class WaterBottle : public Container
{
public:
WaterBottle() : Container() {}
void open()
{
TurnLid();
}
~WaterBottle();
};
class ExerciseBottle : public Container
{
public:
ExerciseBottle() : Container() {}
void open()
{
BendStraw();
}
~ExerciseBottle();
};
But the client doesn't know what ExerciseBottle's implementation of ExerciseBottle's open() is. It calls BendStraw(), which then sets straw_open to true. But ExerciseBottle simply calls one function to do all of this work. The client doesn't have to perform several actions that are used in the implementation of open(). The case goes similarly for WaterBottle. And that's what abstraction is: letting the client know that the back-end will do all of the work for it. When the term "separating implementation from interface" is used, this is what is meant.
Is the complex system that uses data details which are easy to interact or encounter with humans, which differ from the way computer system stores such as in binary number system.
Answered by Neema, Rohan and Upendo (The programmers)
The technique of limiting the data attributes according to given scenario for development of software and removing all irrelevant attributes.This makes software development simpler.
Let's take one real life example of a TV which you can turn on and off, change the channel, adjust the volume, and add external components such as speakers, VCRs, and DVD players BUT you do not know it's internal detail that is, you do not know how it receives signals over the air or through a cable, how it translates them, and finally displays them on the screen.
It refers to the act of representing essential feature without including the background detail or the explanation
It is difficult to find day to day life example of DATA abstraction. However, any data types in programming language, tables and view in DBMS, data structures like LinkedList, List, Queue, Stack are data abstractions. These abstractions provide you the way to access the data in particular manner.
This article may help you understand data abstraction and control abstraction in depth. It also has some of the real life examples of control and data abstractions.
Abstraction rrefers to the act of representing essential features without including the background detail or explanation.
Simply Data Abstraction is nothing but the hiding unnecessary datails from user.
Example:Person simply just wants to make a call, he just select or dial no. and click on call button this info. is enough for him.He dont want to know about how connection is made and whatever process behind making call or how voice is transferred.
I know this question was asked long time ago. But still like to share one real life example which might help others to understand concept of abstraction very easily.
A real-world analogy of abstraction might work like this: You (the object) are arranging to meet a blind date and are deciding what to tell them so that they can recognize you in the restaurant. You decide to include the information about where you will be located, your height, hair color, and the color of your jacket. This is all data that will help the procedure (your date finding you) work smoothly. You should include all that information. On the other hand, there are a lot of bits of information about you that aren't relevant to this situation: your social security number, your favorite football players all are irrelevant to this particular situation because they won't help your date to find you.
Data Abstraction:
It is used to provide the necessary information to the user and hide the unnecessary information from the user. It is called data abstraction.
It will hide your business logic from outside the world.
Technical Example: Console.WriteLine();
Non Technical Example: TV remote, Car Remote.
More Detail: Data Abstraction with real-time example
data hiding deals the security features of oops. according to this property private data member of a class is accessible or visual only inside the class not outside the class.
Most of the books on object-oriented programming I've read used either a Shape class with a Shape.draw() member function or a Dog class with a Dog.talk() member function, or something similar, to demonstrate the concept of polymorphism. Now, this has been a source of confusion for me, which has nothing to do with polymorphism.
class Dog : public Animal
{
public:
...
virtual void talk() { cout << "bark! bark!" << endl; }
...
};
While this might work as a simple example, I just can't imagine a good way to make this work in a more complicated application, where Dog.talk() might need to access sound subroutines of another class, e.g. to play bark.mp3 instead of using cout for output. Let's say I have:
class Audio
{
public:
...
void playMP3(const string& filename)
...
};
What would be a good way to access Audio.playMP3() from within Dog.talk() at design time? Make Audio.playMP3() static? Pass around function pointers? Have Dog.talk() return the filename it wants to play and let another part of the program deal with it?
One way might be to have the Dog constructor take a reference to an instance of an Audio class, because dogs (usually) make noise:
class Dog: public Animal {
public:
Dog(Audio &a): audio(a) {}
virtual void talk() { audio.playMP3("bark.mp3"); }
private:
Audio &audio;
};
You might use it like this:
Audio audioDriver;
Dog fido(audioDriver);
fido.talk();
My solution would be for the Dog class to be passed an audio device in the bark function.
The dog should not store a pointer to the audio device all the time, that's not one of its responsibilities. If you go that route, you end up with the constructor taking two dozen objects, essentially pointing to all the rest of the application (it needs a pointer to the renderer too, so it can be drawn. It needs a pointer to the ground, and to the input manager telling it where to go, and........... Madness lies that way.
None of that belongs in the dog. If it needs to communicate with another object, pass that object to the specific method that needs it.
The dog's responsibility is to bark. A bark makes a sound. So the bark method needs a way to generate a sound: It must be passed a reference to an audio object. The dog as a whole shouldn't care or know about that.
class Dog: public Animal {
public:
virtual void talk(Audio& a);
};
By the same logic, shapes should not draw themselves. The renderer draws objects, that's what it's for. The rectangle object's responsibility is just to be rectangular. Part of this responsibility is to be able to pass the necessary drawing data to the renderer when it wishes to draw the rectangle, but drawing itself is not part of it.
This is a really interesting question as it touches on elements of design and abstraction. For example, how do you put a Dog object together so that you retain control over how it is created? What sort of Audio object should it support and should it 'bark' in MP3 or WAV etc?
It's worth reading through a bit about Inversion of Control and Dependency Injection as a lot of the issues you're thinking about have been thought through quite a bit. There are quite a few implications such as flexibility, maintainability, testing etc.
The callback interface has been suggested in a few of the other answers, but it has drawbacks:
Many (potentially significantly) different classes relying on the same interface. These classes different needs may corrupt the clarity of the interface, what started out as PlaySound( sound_name ) becomes PlaySound( string sound_name, bool reverb, float max_level, vector direction, bool looping, ... ) with a bunch of other methods (StopSound, RestartSound, etc etc)
Changes to the audio interface will rebuild everything that knows about the audio interface (I find this does matter with C++)
The provided interface only works for the audio system (well, it should only be for the audio system). What about the video system, and the networking system?
One alternative that has also been mentioned is to make the audio system calls static (or the audio system interface a singleton). This will keep dog construction simple (creating a dog no longer requires knowledge of the audio system), but doesn't address any of the issues above.
My prefered solution is delegates. The dog defines its generic output interface (IE Bark( t_barkData const& data); Growl( t_growlData const& data ) ) and other classes subscribe to this interface. Delegate systems can become quite complex, but when properly implemented they are no more difficult to debug than a callback interface, reduce recompile times, and improve readability.
An important note is that the dog's output interface does not need to be a separate class that the dog is provided with at construction. Instead pointers to the dogs member functions can be cached and executed when the dog decides it wants to bark (or the shape decides to draw).
A great generic implementation is QT's signals and slots, but implementing something so powerful yourself will prove difficult. If you would like a simple example of something like this in c++ I would consider posting one but if you're not interested I'm not going to take the time out of my Saturday :)
Some drawbacks to delegates (off the top of my head):
1. Call overhead, for things that happen thousands of time a second (IE "draw" operations in a rendering engine) this has to be taken into account. Most implementations are slower than virtual functions. This overhead is utterly insignificant for operations which do not happen extremely frequently.
2. Code generation, mostly the fault of C++'s limited pointer-to-member-function support. Templates are practically a requirement to implement an easy to read portable delegate system.
Mainly depends on what your application is. Passing function pointers to the animals is not a good idea unless you want dogs and cats to use different audio drivers.
The approach with the static playMP3 method is fine. Using a global reference for your audio system is perfectly fine.
A basic answer is that an Animal gets initialized with either an Audio object or a more complex object that contains multiple Audio's. An Animal's talk function then calls a method on this Audio object to produce the talk noise for the animal.
The Dog object initializes the Animal with a particular instance of an Audio object characteristic of Dogs, or (in more complex cases) takes parameters that allow it to build the Audio object to pass to Animal.