开发者

Define C++ class in Java way

开发者 https://www.devze.com 2023-02-05 18:52 出处:网络
This maybe not a real question, please close this if it\'s not appropriate. In C++, 开发者_如何学Cyou can\'t mix the header and implementation in a single source file, for example, the class will be

This maybe not a real question, please close this if it's not appropriate.

In C++, 开发者_如何学Cyou can't mix the header and implementation in a single source file, for example, the class will be defined twice if it's included by a.cc and b.cc:

// foo.h
#pragma once
class Foo {
    void bar() { ... }
};

// a.cc
#include "foo.h"
class A {};

// b.cc
#include "foo.h"
class B {};

// Link all
$ gcc -c -o a.o a.cc
$ gcc -c -o b.o b.cc
$ gcc -o main main.cc foo.o a.o b.o

Then, it's ambiguous Foo::bar() in three object files!

Instead, we must separate the implementation into another file:

// foo.h
#pragma once
class Foo {
    void bar(); 
};

// foo.cc
# include "foo.h"
void Foo::bar() { ... }

Though maybe not a big problem, because usually the binary for foo::bar() in a.o and b.o are the same. But at least there's some redundant declarations, isn't it? And some more confusions caused by this redundant:

  1. Where to give the default values for the optional parameters?

    class Foo {
        void bar(int x = 100); 
    };
    

    or,

    void Foo::bar(int x = 100) { ... }
    

    or, both?

  2. Move between inline & not inline...?

    If you want to switch a non-inlined function to an inlined one, you should move the code from foo.cc to foo.h, and add the inline keyword prefix. And maybe two seconds later, you are regreted what you've done, then, you move out the inlined one in foo.h back to the foo.cc and remove the inline keyword again.

    But you won't need to do so if the declaration and definition are sit together.

And there are more of this kind of minor headaches.

The idea is, if you write the definition of a function just along with the declaration, along, there is no way a compiler couldn't infer the prototype of a function.

For example, by using a single source.cc, and import the type information only, for example,

#include_typedef "source.cc"

Things will be simpler. It's easy for a compiler to ignore variable allocation and function definitions by just filter at parser time even before constructing the AST, isn't it?

I'm used to programming in separate source/header files, I'm certainly capable of doing the separation. You can argue on the programming style, but, that will degrade the question what's the correct way to represent logics to what's the better programming style. I don't think Java is a better programming style, in the source/header separation context. but Java gives the correct way.

Do you mind to separate the headers from the classes? If possible, how will you mix them into one? Is there any C++ front-end which can separate the declaration and definition into separate files from mixed sources? Or how can I write such one? (I mean to the GCC.)

EDIT

I'm not bashing C++ anyway, I'm sorry if you get wrong information from this question.

As C++ is a multi paradigm language, you can see how MFC setup message bindings by using magic macros, and how boost library implements everything using templates. When the separation comes into the problem domain, (Thanks to ONeal pointing out the domain belongs to the packaging system) one can try to figure out how to resolve it. There are so many kinds of programming styles, to me, because I have spent so much time on programming C++, so any small convenient accumulates to a big convenient. Write implementation along with the declaration is one of the convenient. I guess I can reduce the source lines by at least 10% if I need not to separate them. You may ask, if convenient is so important, why not just use Java? It's obviously that C++ is different with Java, they are not interchangeable.

inline function maybe resolve the problem, but it changes the semantics at all.


With this question, you're bringing up a fundamental shortcoming of the usual C++ compilation system. But letting that asside, there seems to be something broken with your first example. Indeed, such a style, where you put all of your classes completely inline, works perfectly well. The moment you start working with templates more in-depth, it is even the only way of making things work. E.g. the boost libraries are mostly just headers, even the most involved technical details are written inline, just because it wouldn't work otherwise.

My guess is that you missed the so called header guards -- and thus got the redefinition warning.

// foo.h --------

#ifndef FOO_H
#define FOO_H

class Foo {
    void bar() { ... }
};

#endif // FOO_H

This should just work fine


Where to give the default values for the optional parameters?

The header file. Default arguments are just syntactic sugar which is injected in at the call site. To give them at the definition point should not compile.

Move between inline & not inline...?

Depends on two things: A. if performance matters, and B. if you have a compiler supporting Whole Program Optimization (both GCC and MSVC++ support this (not sure about others); also called "link-time code generation"). If your compiler does not support LTCG, then you might get additional performance by putting the function in the header file, because the compiler will know more about the code in each translation unit. If performance doesn't matter, try to put as much as possible into the implementation file. This way, if you change a bit of implementation you don't need to recompile your whole codebase. On the other hand if your compiler supports LTCG then it doesn't matter even from a performance prospective, because the compiler optimizes across all translation units at once.

Do you mind to separate the headers from the classes? If possible, how will you mix them into one?

There are advantages both Java/C#/friends' package system, and there are advantages to C++'s include system. Unfortunately for C++, machines with mountains of memory have made Java/C#'s system probably better. The issue with a package system like that is that you need to be able to keep the entire program structure in memory at any given time, whereas with the include system, only a single translation unit needs to be in memory at any given time. This might not matter for the average C++ or Java programmer, but when compiling a codebase of many millions of lines of code, doing something like what C++ does would have been necessary for extremely under-spec'd machines (like those C was designed to run on in 1972). Nowadays it's not so impractical to keep the program database in memory though, so this limitation no longer really applies. But we're going to be stuck with these sorts of things, at least for a while, simply because that's the way the language is structured. Perhaps a future revision of the C++ language will add a packaging system -- there's at least one proposal to do so after C++0x.

As for "mind" from a programmer prospective, I don't mind using either system, thouch C++'s seperation of the class declaration and function definition is sometimes nice because it saves an indentation level.


The first example you gave is in fact perfectly valid C++; the functions defined in the body of the class declaration are the same as if you had defined them as inline. You must have included the .h twice without some kind of include guards to make sure the compiler only saw the first copy. You may have seen this before:

#ifndef FOO_H
#define FOO_H 1
...
#endif

This ensures that if foo.h is included twice that the second copy sees that FOO_H is defined and thus skips the code.

Default parameters must be declared in the .h, not the .cc (.cpp). The reason for this is that the compiler must create a full function call with all parameters when the calling code is compiled, so it must know the value of any default parameters at the point of the call. Defining them again in the code definition is redundant and generally treated as an error.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号