Actually deploying a contract is not analogous to instantiating an object.
Deploying a contract is like publishing your program for the world to see/use. So when you deploy your contract you create a version of it and publish it. The difference is that regular programs are typically available at the same "address" with a new version but deploying the same contract (or its updated version) never uses the same address.
However you can instantiate a new contract from within an existing contract by doing something like MyContract contr = new MyContract() and this creates the contract and its address (and functionality) is accessible from the contr variable. Or you can simply create a reference to an existing contract with MyContract contr = MyContract(address_of_contract). In both cases the contr variable is an instance of the contract and is therefore analogous to the OOP world's instantiation.
So, yes, if you create contract within a contract you can store those addresses within an array.