I'm confused by both of what you're saying.
But please consider how often do you iterate over your single array and access *all* of its elements sequentially? I do it only a single time: upon level creation when everything is zeroed out. If you scan your array only looking for monsters (for example to resolve grenade explosion) this is actually slower because you read larger chunks of data (a whole structure) of which you only need the pointer to monster.
The oop method is to make it so that all objects inherit from a base object class typically starting with two other abstracts: passive and active (not really necessary). All objects in the active list are polled each turn for an action (active.update), whereas passive is the set of objects that can only be acted upon (walls, items, etc, static gas). For collision detection, we might have a map[x.y] where each element is a list of pointers to the objects that occupy that square (in order of graphical display). This has the double benefit of allowing us to cache the screen on update, making it so we only need to redraw tiles that have actually changed (I assume this is happening no matter what method a person is using).
When an explosion takes place, all we care about are the map tiles that the explosion collides with (whether it is a state-progression over several turns or resolved immediately). We only need to check the salient tiles (radius from origin), not entire lists or the entire map.
In this way, I agree that the tile shouldn't be an object, but if we have destructible walls then every tile will at least have a floor/pit. Regardless, I don't see why you need to even think about 'whole' anythings.
If you scan your array only looking for monsters (for example to resolve grenade explosion) this is actually slower because you read larger chunks of data (a whole structure) of which you only need the pointer to monster.
Checking the type of object is too resource-intensive? You're not reading the whole structure, just checking the pointer for what type of data structure it points to. That's pretty harmless. With a nice inheritence hierarchy you can very intelligently search for types of things.
This is unfathomable to me. Perhaps the problem is c vs. c++. Of course the methods in c++ are what leads me to question why anyone would use c except for the sake of faster execution with computationally heavy projects. It's much easier to have tile[x,y].draw instead of for instance ground[x,y].draw; creature[x,y].draw; item[x,y].draw; ... etc. When you add a new layer, you can just edit the tile.draw method instead of adding a new array everywhere that arrays get accessed. This is also where the accessing a lot of arrays i mentioned comes into play. However if your game is ascii or otherwise single-layered than I guess it is not a big deal.
The use of a tile object in your description is somewhat confusing to me-- is it strictly a container for the objects occupying the tile?
Each object is mapped to or references an asset (whether it is a character, a tile, or a sprite). We keep our rendering and logic separate, so we have separate update and render methods. A call to render tells the graphics to draw the calling object at its position (pending on how/if we buffer, we need to update all effected tiles- such as those that the object is leaving). If we think of things as a scenegraph, the object is parented to its location-- in this case, a tile object. The tile object orders its children in regards to each child's type (the ancestor class for monster will have precedence over the ancestor class for ground-- we want to preserve awareness so we can accurately draw backgrounds if necessary- similarly, some ancestors, like gas or lights, will have even higher precedence being drawn as filters).
Given this paradigm, we ONLY need to call the render method of a tile whenever the appearance of a child changes (highlighting/fading gas/etc) or when a child is gained/lost (movement/death/etc).
Is this more or less what you're talking about?
You can't compute a conjunction of map monster array and array representing inverted annihilation cloud to get rid of all creatures in certain area. Shifting clouds can be done with simple celluar automaton which does not need to know the map holds monsters, items and so on if you have separate array for gases. Otherwise it does because you pass the whole structures.
Why would you want to perform a conjunction? Is it significantly more efficient than collision detection? I would assume no, since CD is going to only check/effect tiles that are directly influenced. A conjunction is going to mash together a whole ton of empty cells or cells that don't have anything to do with the annihilation cloud (unless you do some fancy culling).
Why not just have a particle engine that manages each cloud-tile object? You never need to know anything apart from which tiles are occupied by gas and what state each gas-tile is in. It's possibly a simpler implementation of an automaton. In the game-loop, the gas-cloud gets updated with every other active object, its own update method calls all of its cloud-tile updates to see what each one does. It makes colliding very light on resources- no arbitrary searching or nonsense. Only tiles that are involved in state-changes get looked at.