Leeor_net is right about object slicing. You'll want to return the value by pointer or reference, rather than by value. If you're going to use smart pointers, I think unique_ptr might be appropriate here. Admittedly though, I have little experience so far with using the smart pointer classes.
If your main objective is to use the dot syntax (archive.method), instead of the arrow syntax (archive->method), using a reference is sufficient. You can convert at multiple locations, such as the new statement, the return statement, or at the point of call. Remember that references use value like notation, even though they are pointers under the hood:
Archive* ptr = new VolFile(fileName);
Archive& ref = *ptr; // You could inline this above
If your main object is to get rid of the delete, you're kind of out of luck with the current design. You have a function that returns a pointer to an object of either type. This has to be done by base class pointer. Since the object is returned by pointer/reference form the function, it's can't be a local variable stored on the stack. That means it's either a heap variable (new/delete), or a global (generally bad practice to use). At best you can hide the new/delete by using smart pointers.
A topic of interest for smart pointers is
Resource Acquisition Is Initialization (RAII).
To avoid the new/delete, and global variables, you'd need a code structure where some function creates the appropriate object on the stack as a local variable, which can pass that object into other methods for future processing, but never returns it. In that way, the lifetime of the object never escapes the function that created it. This isn't always appropriate.
I have no qualms about checking file extensions in your code to guess the archive type. At any rate, the archive code already checks the file headers after trying to open the files. The code will throw an exception if it's opened by an archive class that doesn't match the file contents. If you wanted, you could use try/catch blocks to try opening the file as both types, and going with the first that succeeds, assuming one of them does. That would make it independent of the extension, though the code is a bit uglier, and this is supporting a use case people should really be avoiding, namely badly named files with misleading extensions.
A lot of languages have keyword delimited blocks, such as being/end:
if someBooleanCondition then begin
blockOfCode
end
If you're used to that format, than the C++ equivalent makes a lot of sense:
if (someBooleanCondition) {
blockOfCode;
}
Some people loathe this format. I used to. Though I began to try it after programming in languages that delimit blocks with keywords. It makes a big difference in the amount of code you have to scroll through. It takes one less line of vertical space, so it's more compact than having braces at matching indentation levels. It also passes all the lint validations for checkers that ensure "if" statements always having matching braces. The linters check this because bugs like the one leeor_net posted are surprisingly common. I too used to be of the opinion that it's quite visually obvious, though it's still possible to get wrong during refactoring due to competing demands for attention when making complicated changes involving many lines of code. This type of bug is not something that is generally written as is. It gets introduced during code changes, since without the braces, the structure of the code is very dependent on how many lines of code you have in a block.