CybeX
CybeX

Reputation: 2406

c++ - using std namespace and dependancies

While trying to familiarize myself with c++ and its concepts, I came across the

using namespace std

and

#include <iostream>

my simple code is as follows

#include "stdafx.h"
#include "ConsoleApplication5.h"
#include <iostream>     

int main()
{
    std::cout << "hi";
    return 0;
}

Using Visual Studio 2015 Community, which uses intellisense, shows

cout

uses the following

std::ostream std::cout

As a C# programmer, this confuses me slightly. Is this :

std::ostream

being a return type while

std::cout

being the method/parameter passed or is

std::ostream

a dependancy of

cout

UPDATE (after Archimaredes Answer)

In C#, one can use the following:

StreamWriter srWrite;

or

StreamWriter srWrite = new StreamWriter(string filepath)

or one can use:

StreamWriter srWrite = new StreamWriter(new NetworkStream(Socket.GetStream()));

Each case a object of type namely

StreamWriter

is assignable with a new object or a existing file or a networkstream.

I tried the same after that which you mentioned (excuse the C# mentality) with the std::ostream x = new std:ostream which returns a no default constructor.

Could you just add how std::ostream and std::cout relate to each other, which creates/initalizes the other. THis concept is still a bit vague to me.

Upvotes: 5

Views: 1219

Answers (2)

Yam Marcovic
Yam Marcovic

Reputation: 8141

There are some basic differences between C++ and C#. One of them is initialization syntax.

In C#, if you want to create a new type U which either equals or is derived from a type T, you generally write:

T t = new U();

For value-types (such as structs), you can also write just:

T t;

Secondly, there's the issue of object lifetime. With reference-types, their lifetime is, generally (excluding garbage collection/finalization, for simplicity), the duration between the point they're created to the point they're inaccessible from anywhere in the program. With value-types, it's the same thing, except that value-types aren't garbage-collected by themselves: they either "vanish" when their defining stack frame returns, or when the reference-type object of which they form part of the memory is "dead."

With C++, it's a different story.

Loosely speaking, you have "raw" types, which are to some extent equivalent to value-types in C#. Then you have pointers and references, which point to such types. There are much better accounts of what's going on here, but I'm trying to explain just enough background to answer your question.

So, if you hypothetically (assuming all types are defined) typed:

StreamWriter sr;

in C++, you'd essentially be constructing a new StreamWriter object, using its default constructor. Hence, the definition is well-formed only if StreamWriter has an accessible default constructor in that context.

If you typed:

StreamWriter sr = new StreamWriter();

You would get a compilation error, because new StreamWriter() returns a pointer to a StreamWriter, which you're trying to assign to an "actual" StreamWriter.

The correct way to type this (excluding best-practices for now), would be

StreamWriter* sr = new StreamWriter();

The important difference between "raw" types and pointers (or references) is indirection. Let's consider what would happen in the following function:

void func() {
    StreamWriter sr = StreamWriter();
}

In this case, the stack frame for func() would actually need to consume enough memory to contain the whole contents of a StreamWriter instance. That is, if it contained 4 32-bit integers as private members, then the stack frame would have to consume at least 16 bytes (4 * (32 / 8)).

So if you wanted to access a member of StreamWriter, there wouldn't be any indirection. You would just read the memory at the offset of that member from the address of your sr variable, because &sr denotes the start of the actual StreamWriter object memory. The implication is that the variable sr can only safely contain StreamWriter, and no derived classes, since those derived classes might require more memory.

This is why I said "raw" types are somewhat equivalent to C# value types, because they share this behavior. If you hadn't noticed it before, C# structs cannot be inherited, and this should also serve to explain, or imply, why that is so.

Now, in the case of the following function, using pointers:

void func() {
    StreamWriter* sr = /*...*/;
}

Then, conceptually (excluding compiler optimizations in eligible cases), if you wanted to access a certain member of sr, you would have to take the following steps:

  1. Read the pointer value of sr.
  2. Infer the desired member variable offset.
  3. Add the result of (1) with the result of (2) to get the address of the member variable contained in that specific StreamWriter instance, or sr.
  4. Read the number of bytes corresponding to the size of that member variable, starting from the value obtained from (3).

And the crucial point here, is that there's an indirection. So if sr now pointed to a derived class of StreamWriter, then its basic memory layout would start out just like StreamWriter's, making the previous function still work correctly. This, by the way, is why polymorphism requires you to use either pointers or references. There's an added level of virtual calls and how that works, but that's a bit out of scope for now.

By now you should realize why

  1. std::ostream s = new std::ostream() is ill-formed.
  2. std::ostream s = std::ostream() or simply std::ostream s wouldn't work—because std::ostream has no accessible default constructor.

What's missing, though, is that my (2) here seems to conflict with your observing:

std::ostream std::cout

And that's because that observation misses a crucial modifier in the actual one, which is:

extern std::ostream std::cout; // note 'extern'

Now, that's not a definition, rather it's a declaration, that—somewhere—there's an initialized variable in the namespace std called cout which is of type ostream, and we leave it to the linker to figure out where, while still being able to make use of that variable.

This is hardly a minimal answer to your question, but I felt like you might benefit from a slightly deeper analysis. Enjoy learning C++!

P.S. I touched on several aspects here which really deserve more attention in and of themselves, as far as proper usage goes. However, that would make this answer even longer.

Upvotes: 3

Archimaredes
Archimaredes

Reputation: 1427

std::cout is an object in global scope, of type std::ostream. When you call std::cout << "hi", you are invoking the operator<<() method with the std::cout object as your left-hand value and the string literal "hi" as the right-hand value.

cout and ostream are inside the std namespace, hence the std:: prefix. If you place using namespace std in your code, this allows you to omit the prefixes, e.g.

#include <iostream>
using namespace std;

int main() {
    cout << "hi"; // no error because the 'std' is unnecessary now
}

Namespaces are used to prevent name conflicts, in exactly the same way as C#; it is usually good practice to not use using namespace directives for entire source code files in order to prevent name conflicts in your code.


Edit in response to OP's update

std::cout is an instance of std::ostream. To illustrate this, consider the following:

class A_Class {
public:
    A_Class() {}
    void foo() {}
};

int main() {
    A_Class an_instance;
    an_instance.foo();
}

A_Class is a class, while an_instance is an instance of A_Class.

Similarly; ostream is a class, while cout is an instance of ostream.


Edit in response to OP's comments

This may look confusing to a user of C#, but it is the exact same concept as:

int n = 5;

Whereint is the type, and n is the variable name.

Upvotes: 7

Related Questions