Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

Что такое CORBA?

CORBA происходит от Common Object Request Broker Architecture. Это стандартный способ взаимодействия различных обьектов, когда они находятся на различных компьютерах или написаны на разных языках.

CORBA реализована различными вендорами, и эти реализации могут быть сделаны в соответствии со стандартными специфиациями.

    Что такое ORB?

    ORB происходит от Object Request Broker. Этот инструмент позволяет членам-функциям вызываться удаленно и быть написанными на различных языках.

    Ключевая фича ORB-прозрачность. Для вызываемой функции не имеет значения,где находится ее обьект-локально или удаленно.

      Что такое IDL?

      IDL происходит от Interface Definition Language. С его помощью можно определять интерфейсы коммуникации между различными сервисами с помощью набора типов данных. IDL-это язык спецификаций,а не реализаций.

        Что такое COS?

        COS stands for Common Object Services. COS is also known as CORBA Services. These services are common components that handle things like creating objects, controlling access to objects, keeping track of relocated objects, managing objects and their relationships, and so on.

        Typical CORBA Services include Naming, Event Management, Lifecycle Management, Transaction Management, Persistence, and Security. CORBA Services provide infrastructure utilities that improve application consistency and programmer productivity.

          Что такое OMA?

          OMA stands for Object Management Architecture. It's the big picture architecture that includes the CORBA (the ORB itself), CORBA Services (COS), and CORBA Facilities. It ties all the pieces together.

            Что такое OMG?

            OMG stands for Object Management Group. OMG is an international consortium with nearly 1000 members, including software vendors, software developers, and users. OMG is the organization that develops and standardizes the OMA.

            This chapter will be deliberately loose in distinguishing between CORBA, OMA, and IDL. Technically, IDL is a proper subset of CORBA, which is a proper subset of OMA, but it is easier to speak of some IDL issues as CORBA questions than it is to use precise wording that could confuse a newcomer to the subject.

              The purpose of this chapter is to help developers and development organizations avoid some of the common pitfalls that can occur when they first encounter distributed objects and CORBA. It is not intended to be a tutorial that provides all the details of using CORBA. Rather, this chapter skims a few of the important issues without getting into technical detail.

              Entire books are devoted to using CORBA, and readers interested in the details of, for example, static versus dynamic member function invocation or the proper use of the _var and _ptr suffixes should consult them. Another good way to get general information on CORBA is to visit the Web site of the Object Management Group (OMG) by pointing the browser at http://www.omg.org.

                FAQ 35.08 What is the most important message of this chapter?

                CORBA is nontrivial and requires serious effort, even for experienced C++ programmers.

                Because CORBA's Interface Definition Language (IDL) looks so similar to C++, many programmers assume that CORBA doesn't have a steep learning curve. This is far from the truth, and most of this chapter is dedicated to showing why there are and have to be major differences between C++ and CORBA. Most of these differences are inherent in any approach to distributed objects.

                Also, it's important to understand where CORBA fits into the grand scheme of things. It offers critical middleware and useful components for developing major systems, but the technical differences between the various CORBA implementations or between CORBA and COM are usually less important than business considerations.

                  FAQ 35.09 What are the important concepts behind CORBA?

                  The key concepts are

                  1. Platform and language independence

                  2. Location transparency

                  3. An OO approach that includes interface inheritance within IDL

                  4. Interoperability between ORBs

                  5. A consensus-based industry standards process

                  CORBA uses the proxy design pattern to achieve platform and language independence. A proxy is a stand-in, or surrogate, for a "real" object and provides a client-side reference for accessing a server object that could be located anywhere. By decoupling the software architecture from the runtime environment, the software becomes more flexible and decisions such as which objects reside where can be controlled by a network administrator armed with time sensitive information that was not available to the software architects when they were designing the system.

                  IDL is a language for defining the interfaces for distributed objects, where the interfaces are similar to C++ abstract base classes with only pure virtual member functions. The IDL compiler converts the interface specification into the target language, which can be C, C++, Java, and so on. CORBA IDL supports interface inheritance, but since it is not an implementation programming language, it does not support inheritance of implementation (however implementation inheritance is normally used when implementing an IDL interface in an OO programming language such as C++).

                  ORBs from different vendors can communicate seamlessly because of the Internet Inter-ORB Protocol (IIOP), which runs over TCP/IP. The specification also supports DCE as the communications layer, but most CORBA users choose TCP/IP.

                    FAQ 35.10 Isn't OMG IDL pretty much the same as C++?

                    No.

                    IDL does have a C++-like syntax, and it includes these familiar features:

                    1. Basic data types such as short, long, float, char and others

                    2. Modules, which are roughly equivalent to namespaces

                    3. Exceptions (but see FAQ 35.13)

                    4. Strings, arrays, enumerations, typedefs, and so on

                    Despite these surface similarities, OMG IDL is not a programming language and it should not be compared as such with C++. OMG IDL is a platform-neutral specification language. OMG IDL can handle only interface issuesparameter types, return types, member function names. It cannot handle implementation issues such as member data, local variables, implementation code, and so on.

                    Furthermore, it is important to note that CORBA data types do not necessarily correspond to the similarly named C++ data types. For example, a long in IDL may or may not be equivalent to the C++ type long. A long in IDL corresponds to the C++ type CORBA::Long. For these reasons, C++ programmers who are implementing CORBA classes should be careful to use the CORBA data types rather than the C++ data types when necessary.

                    As another paradigm shift, C++ programmers who are new to CORBA tend to think of the CORBA data types (such as CORBA::Long) as aberrations that should be avoided. In reality the opposite is true: in a sense the CORBA data types are more portable than the C++ data types. For example, sizeof(CORBA::Long) is guaranteed to be 4 bytes on all hardware platforms, whereas sizeof(long) might not be exactly 4 bytes.

                    In addition, CORBA is responsible for transmitting data values between various machines as necessary and dealing with any data transformation issues. For example, different machines use different byte orderings for different data types (little-endian vs. big-endian), and different programming languages may represent data types differently than C/C++ does. CORBA manages all this transparentlyif the communicating objects are on different machines it uses a process called marshaling to convert the source machine's binary data to the IIOP (the standard usually allows others) representation, and a reverse process called demarshaling to convert the IIOP representation at the destination machine back into the appropriate binary data format. One performance trick used by some ORBs is recognizing situations where these processes can be safely eliminated (such as when everything is on the same hardware and the networking can be avoided). Other performance tricks, such as receiver-makes-right semantics, avoid unnecessary message translations between machines using the same byte order and are part of the specification.

                    Finally, C++ programmers need to be aware that IDL does not support overloading, overriding, or pointers. So even though IDL looks a lot like C++, there are significant differences.

                      FAQ 35.11 Is the lifecycle of a CORBA object the same as the life cycle of a C++ object?

                      No, and this requires some mental adjustments.

                      C++ programmers are accustomed to objects that are known to exist because they were instantiated or known to be gone because they went out of scope or were programmatically destroyed. Reference counting (see FAQ 31.09) confuses the argument a little, but the point remains that the programmer feels that the object life cycle is under control.

                      But in the CORBA world, objects, particularly server objects, have much more of a life of their own. They may be brought to life once and never die because there is no scope to exit, or they may have a brief existence and commit suicide. It is also important to distinguish between a CORBA object and a servant for a CORBA object. A servant is a C++ object that provides the body of and implementation for one or more CORBA objects, and many C++ programmers stumble over this distinction. A CORBA object may have one or more servants, and a servant may represent (the proper technical term is incarnate) several CORBA objects during its lifetime. These are separate but related entities.

                      Emotionally, the experience for most developers is similar to the one they went through when they made the shift from procedural programming to OO. For example, programmers coming to OO (say, programming in C++) from procedural programming (say, programming in C) usually feel that they have "lost control." The top-down command-and-control approach was part of their old programming paradigm, and an adjustment is needed. Eventually this uncomfortable feeling disappears. The shift from a traditional monolithic program to a distributed program is similar.

                        FAQ 35.12 Is the C++ code that interacts with the CORBA implementation portable to a different CORBA vendor?

                        No, but the situation has improved because of the Portable Object Adapter.

                        Unfortunately, the C++ code that interacts with a particular CORBA vendor's ORB is not 100% portable to other CORBA vendors. In the early days, it could be said that there was no such thing as a CORBA programmer, there was only an Orbix programmer or a VisiBroker programmer. Many of the differences between CORBA implementations could be traced to the imprecise specification of the Basic Object Adapter (BOA). In mid-1997, the OMG adopted a replacement for the BOA called the Portable Object Adapter (POA). The POA specification is voluminous as well as precise, and it introduces some unifying concepts for the CORBA paradigm. The reader is cautioned that many excellent CORBA books were written prior to POA and that BOA has been deprecated.

                        The net result of the POA is that conforming vendors do not have the same latitude as before and programs are much more portable than in earlier days.

                          FAQ 35.13 How do CORBA exceptions compare to C++ exceptions?

                          The same but different.

                          At first glance, it appears that C++ exceptions and CORBA/IDL exceptions are pretty much the same. For example the semantics of instantiating, throwing, and catching exceptions are the same as with C++. But there are also differences, some minor and some major.

                          As a minor difference, C++ implementations of CORBA member functions need to include CORBA::SystemException in the exception specification (either that or not have an exception specification; see FAQ 9.04). Another minor difference is that each CORBA exception must be mapped to a C++ class that inherits from the abstract base class CORBA::UserException.

                          A major difference is that normal C++ exceptions have both state and behavior but CORBA/IDL exceptions cannot have behavior. This is because exceptions may get transmitted to different computers (e.g., when the thrower and the catcher are on different computers), and it's easier for the implementation to copy pure data objects than to copy objects that have behavior.

                          IDL does not support inheritance in exception declarations, which can get quite nasty when C++ exceptions are mapped to CORBA exceptions and then thrown around the ORB. Finally, memory management is tough enough with C++ exceptions, but the issues become substantially more complex in a distributed object environment.

                          So don't be lulled into a false sense of security because the words sound the same. Exceptions in CORBA require a great deal of knowledge and experience that the beginner does not have and does not gain easily, no matter how familiar they are with C++ exceptions.

                            FAQ 35.14 Which CORBA implementation is best? Is CORBA better than COM?

                            The fact that these questions are being asked implies that they do not have a clear answer.

                            If there were one approach to distributed objects that was clearly superior in all respects, then the market would have gravitated to that solution and people wouldn't be asking, "Which is better?" But that hasn't happened, and there is probably a time and place for each alternative to shine. Many articles have been written about the technical differences between CORBA and COM, and each CORBA implementation has its voluble critics and fans. And there are intelligent technical people on both sides of these arguments.

                            So the only proper answer to the question is to take an open-minded approach to ORB selection, as opposed to the one-size-fits-all mentality favored by bigots. Also, it is important to recognize that, in many cases, the technical differences can be relatively small and that nontechnical issues such as pricing, vendor support, or training may dominate the decision process.

                              FAQ 36.01 What are the main issues when mixing C and C++ code in the same application?

                              The main issues are

                              • The C++ compiler must be used to compile main(). This allows the C++ compiler to deal with static initialization and other magical things that main() has to do.

                              • The C++ compiler must be used to direct the linking process. This allows the C++ compilers to deal with templates and also to link in the C++ libraries.

                              • The C++ and C compilers probably need to come from the same vendor and have compatible versions. This allows them to use compatible calling conventions in the binary code they generate, as well as compatible naming strategies.

                              • The interface between the two languages should be kept reasonably small. For example, it is unwise to enable all C++ routines in the entire application to be callable from C, although the reverse is possible: it is not that hard to make C routines callable by C++.

                              • Read the rest of this chapter for more details.

                                FAQ 36.02 How can C++ code call C code?

                                The C++ compiler must know that the function has C linkage.

                                If the C code that is being called is part of the standard C library, such as printf(), simply #include the correct C header file and call the function.

                                If the C code that is being called is not part of the standard C library, the C++ compiler needs to know that the function is a C function. To do this, include an extern "C" block in the C++ code, and declare the C function inside the block:

                                 extern "C" {
                                   void myCfunction(int x, int y);
                                   void yourCfunction(float z);
                                 }
                                 

                                An entire C header file can be included within an extern "C" block:

                                 extern "C" {
                                   #include "my-C-header.h"
                                   #include "your-C-header.h"
                                 }
                                 

                                If a C header file needs to be included in a lot of C++ locations, the extern "C" part can be moved into the header file, provided only the C++ compiler sees it. The simplest way to do this is to create a new C++ header file that includes the C header file. For example, if "Fred.h" is a C header file, "Fred.hpp" can be created as a C++ header file:

                                 // Fred.hpp
                                 extern "C" {
                                   #include "Fred.h"                                  <-- 1
                                 }
                                 

                                (1) Include the C header file

                                The other approach is to modify the C header file directly, which obviously can only be done if the team has control over the C header file. The extern "C" part is wrapped in an #ifdef to make sure these lines are seen only by C++ compilers, not by C compilers. The idea is simple: insert the following lines near the top of the C header file.

                                 #ifdef __cplusplus
                                   extern "C" {
                                 #endif
                                 

                                Then insert the following near the bottom of the C header file.

                                 #ifdef __cplusplus
                                  }
                                 #endif
                                 

                                This works because the C++ compiler automatically #defines the preprocessor symbol __cplusplus.

                                  FAQ 36.03 How can C code call C++ code?

                                  The C++ compiler must be told to compile the C++ function using C-compatible calling conventions (also known as C linkage). This is done using the same extern "C" construct as detailed in the previous FAQ, only this time the extern "C" goes around the declaration of the C++ function rather than the declaration of the C function. The C++ function is then defined just like any other C++ function:

                                   // This is C++ code
                                   extern "C" {
                                     void sample(int i, char c, float x) throw();
                                   }
                                   
                                   void sample(int i, char c, float x) throw()
                                   {
                                                                                        <-- 1
                                   }
                                   

                                  (1) The C++ code that defines the function goes here

                                  The extern "C" declaration causes the C++ compiler to use C calling conventions and name mangling. For example, the C++ compiler might precede the name with a single underscore rather than the usual C++ name-mangling scheme.

                                  The C code then declares the function using the usual C prototype:

                                   /* This is C code */
                                   void sample(int i, char c, float x);
                                   
                                   void myCfunction()
                                   {
                                     // ...
                                     sample(42, 'a', 3.14);
                                     // ...
                                   }
                                   

                                  There can be overloaded C++ functions with the same name as the function that was exported to the C code, but only one of these overloads can be declared as extern "C". Also, the C code cannot access more than one of these since C doesn't support name overloading. Member functions cannot be exported to C code using the extern "C" syntax.

                                    FAQ 36.04 Why is the linker giving errors for C functions called from C++ functions and vice versa?

                                    main() should be compiled with the C++ compiler, and the C++ compiler should direct the linking process.

                                    The C++ compiler should be used to compile main() because it normally embeds C++-specific operations inside the compiled code (for example, to deal with static initialization; see FAQ 2.10). The C++ compiler should direct the linking process since it needs to deal with things such as C++ libraries, static initialization, and templates.

                                      FAQ 36.05 How can an object of a C++ class be passed to or from a C function?

                                      With a layer of glue code.

                                      The example that follows is a bilingual header file, readable by both a straight C compiler and a C++ compiler. Bilingual header files often use the preprocessor symbol __cplusplus, which is automatically #defined by C++ compilers but left undefined by C compilers.

                                       /****** Bilingual C/C++ header file: Fred.h ******/
                                       #ifdef __cplusplus
                                         class Fred {
                                         public:
                                           Fred() throw();
                                           void wilma(int i) throw();
                                         protected:
                                           int i_;
                                         };
                                         inline Fred::Fred() throw() : i_(0) { }
                                         inline void Fred::wilma(int i) throw() { i_ = i; }
                                       #else
                                         struct Fred {
                                           int i_;
                                         };
                                         typedef  struct Fred  Fred;
                                       #endif
                                       
                                       #ifdef __cplusplus
                                         extern "C" {
                                       #endif
                                       
                                       extern void cppCallingC(Fred* p);
                                       extern void cCallingCpp(Fred* p, int param);
                                       
                                       #ifdef __cplusplus
                                         }
                                       #endif
                                       

                                      The function cCallingCpp() might be defined in a C++ file, such as c++-code.cpp.

                                       // This is C++ code
                                       #include "Fred.h"
                                       
                                       void cCallingCpp(Fred* p, int param) throw()
                                       { p->wilma(param); }
                                       

                                      The function cppCallingC() might be defined in a C file, such as c-code.c.

                                       /* This is C code */
                                       #include "Fred.h"
                                       
                                       void cppCallingC(Fred* p)
                                       { cCallingCpp(p, 3); }
                                       

                                      A Fred might be passed to the C code by main() (recall that main() must be compiled by the C++ compiler).

                                       // This is C++ code
                                       #include "Fred.h"
                                       
                                       int main()
                                       {
                                         Fred x;
                                         cppCallingC(&x);
                                       }
                                       

                                      Note that C code should not cast pointers that refer to C++ objects because doing so can introduce errors, especially in cases where the pointer is returned to C++. For example, most compilers adjust the pointer during certain pointer casts involving multiple and/or virtual inheritance; the C compiler doesn't know how to do these adjustments.

                                      The example assumes that the C compiler supports ANSI-C function prototypes. Use #ifdef __STDC__ for those rare legacy situations that require selecting code that supports only the outdated K&R C style.

                                        FAQ 36.06 Can a C function directly access data in an object of a C++ class?

                                        Sometimes.

                                        First read the previous FAQ on passing C++ objects to and from C functions. A C++ object's data can be safely accessed from a C function if all of the following are true.

                                        • The C++ class has no virtual base classes anywhere in the inheritance graph.

                                        • The C++ class has no virtual functions (including inherited virtual functions).

                                        • The C++ class has all its data in the same access level section (private:, protected:, or public:).

                                        • The C++ class has no fully contained member objects that have either virtual functions or virtual base classes.

                                        If the C++ class or its member object have any base classes, accessing the data will be technically nonportable, because the language does not mandate a specific class layout in the presence of inheritance. However, at least with nonvirtual inheritance, all C++ compilers do it the same waythe base class subobject appears first (in left-to-right order in the event of multiple inheritance), and member objects follow.

                                        If the class has any virtual base classes, it is more complicated and even less portable.

                                        By far the safer and easier way is to use an access function from C. This costs a function call to access the datum (that is, these calls from C cannot be inlined), but unless the application is CPU bound (see FAQ 33.02), it is probably best to make the application code safe and portable.

                                          FAQ 36.07 Can C++ I/O (<iostream>) be mixed with C I/O (<stdio.h>)?

                                          Yes, but be careful.

                                          <iostream> and <stdio.h> can be used in the same program. The easiest way to mix them is to make sure that no single file is manipulated using both <iostream> routines and <stdio.h> routines.

                                          If any given file needs to be manipulated by both <iostream> routines and <stdio.h> routines, special considerations must be taken into account. Make sure that ios::sync_stdio(false) has not been called. If it has then call ios::sync_with_stdio() as shown.

                                           #include <iostream>
                                           #include <cstdio>
                                           using namespace std;
                                           
                                           int main()
                                           {
                                             ios::sync_with_stdio();                            <-- 1
                                             // ...
                                           }
                                           

                                          (1) No I/O should occur before this line

                                          Note that this synchronization can degrade I/O performance, so it should be used only if <iostream> routines and <stdio.h> routines are manipulating the same file. For example, synchronization is needed if the program reads from both cin and stdin, or if it writes to both cout and stdout. But if <iostream> routines and <stdio.h> routines are not manipulating the same file, synchronization is unnecessary and should not be used.

                                            FAQ 36.08 Which is safer: <iostream> or <stdio.h>?

                                            <iostream> is safer than <stdio.h> due to improved type safety and less redundancy.

                                            The <stdio.h> functions scanf() and printf() are interpreters of a tiny language, made up mainly of "%" fields (format specifiers). These functions select the correct I/O primitive at runtime by assuming that the format specifier and the actual argument are compatible; if they aren't compatible, garbage is printed or the program crashes. Thus, the programmer is required to provide duplicate information in the format specifier and the actual argument.

                                            The <iostream> routines are different. Users provide only the object to be read or written; the compiler selects the correct I/O primitive at compile time via the rules of function overloading. Therefore, <iostream> is type safe since the selected primitive is always compatible with the actual argument.

                                              FAQ 36.09 Which is more extensible: <iostream> or <stdio.h>?

                                              <iostream> is more extensible than <stdio.h> since <iostream> allows I/O with user-defined types as well as built-in types.

                                              The <stdio.h> functions scanf() and printf() work with a predefined set of types. In contrast, <iostream> allows new, user-defined data types to be written and read using the same syntax used for built-in types (that is, using << and >>). This extensibility is analogous to adding new "%" fields to the switch statement that is used within the implementation of scanf() and printf().

                                              C++ allows user-defined types (class types) to look and act like built-in types.

                                                FAQ 36.10 Which is more flexible: <iostream> or <stdio.h>?

                                                <iostream> is more flexible than <stdio.h> since <iostream> separates the code that formats an object from the code that performs I/O of the character stream produced or consumed by formatting. This separation allows replacement of the underlying I/O mechanisms without the need to rewrite the formatting code.

                                                For example, <iostream> uses real classes, hence users can create derived classes. User-defined types can thus look and act like streams but don't necessarily have to use the same underlying I/O mechanisms. The formatting code written for both user-defined and built-in types works correctly with these new classes.

                                                  FAQ 36.11 Why does it seem that C++ programming feels farther away from the machine than C?

                                                  Because it is.

                                                  Because C++ is an object-oriented programming language, it is designed to allow the creation and manipulation of objects from the problem domain. Thus, C++ allows programmers to operate at a higher level of abstraction: there is effectively a greater distance between the software and the machine. This higher level of abstraction allows programmers to develop software in the language of the problem domain rather than in the language of the computer. It is considered a feature, not a bug.

                                                    FAQ 36.12 Why does C++ do more things behind your back than C does?

                                                    Because the goal of programming in C++ is different than the goal of programming in C.

                                                    One of C's great strengths is that it has no hidden mechanism. What you see is what you get. You can read a C program and see every clock cycle.

                                                    This is not the case in C++. As an OO programming language, C++ has different goals than C. For instance, C++ calls constructors and destructors to initialize objects. Overloaded operators are another case in pointthey provide a level of abstraction and economy of expression that lowers maintenance costs without destroying runtime performance. Longtime C programmers are often ambivalent about these features, but they soon realize their benefits.

                                                    Naturally, bad code can be written in any language. C++ doesn't guarantee any particular level of quality, reusability, abstraction, or any other measure of goodness.

                                                    C++ enables reasonable developers to write superior software. It doesn't make it impossible for bad programmers to write bad programs.

                                                      FAQ 37.01 What are private inheritance and protected inheritance?

                                                      Has-a, not is-a. From the user's perspective, private and protected inheritance are semantically similar to composition, but they are very different from normal public inheritance. Thus private and protected inheritance are a lot more like "has-a" than "is-a" (more precisely, than "is-substitutable-for"). Here are the ways they are like "has-a".

                                                      Like normal composition, private and protected inheritance cause an inner object to be contained inside the outer object. With normal composition, this inner object is called a member object. The syntax for doing this with private inheritance is different than the syntax for normal composition, but the idea is the same. With private and protected inheritance, the inner object is called the base class subobject. The important thing to note is that the outer object contains the inner object in both cases.

                                                      Like normal composition, private and protected inheritance prevent normal users from directly accessing the inner object. With normal composition, this is normally done by declaring the member object in the private: or protected: part of the outer object. The syntax for doing this with private inheritance is different than the syntax for normal composition, but the idea is the same. With private and protected inheritance, this encapsulation is done automagically: normal users are not allowed to convert a derived pointer to its private or protected base class pointer (this is different than the normal is-a conversion of public inheritance; see FAQ 2.24).

                                                      Like normal composition, private and protected inheritance allow the outer object to select specific features of the inner object that users can access, and users are prevented from accessing any other features than the ones explicitly allowed by the outer object. With normal composition, the outer object grants normal users access to specific features of the inner object by call-through member functions. A call-through member function is often a one-line function that simply calls the corresponding member function on the inner object. The syntax for doing this with private inheritance is different than that for normal composition, but the idea is the same. With private and protected inheritance, there are two options: either the derived class defines a call-through member function that calls the corresponding member function of the private or protected base class subobject, or the using syntax can be used to make a base class member function public: (see FAQ 37.06).

                                                        FAQ 37.02 What is the difference between private inheritance and protected inheritance?

                                                        In private inheritance, the relationship with the base class is a private decision; only members and friends of the privately derived class can exploit the relationship with the private base class. In protected inheritance, the relationship with the base class is a protected decision, so members and friends of the protected derived class and members and friends of classes derived from the protected derived class can exploit the protected inheritance relationship, but normal users cannot.

                                                        Protected inheritance is less restrictive than private inheritance and therefore introduces more coupling between the derived class and the base class. With protected inheritance, if the relationship between the protected base class and the derived class is changed (or if the protected operations of the protected base class change), the effects may reach beyond the protected derived class and its friends to classes derived from the protected derived class, classes derived from those derived classes, and so on.

                                                        This is a for-better-for-worse situation; derived classes have more coupling, but they also have the ability to exploit the relationship between the derived class and the base class.

                                                          FAQ 37.03 What is the syntax and semantics for private and protected inheritance?

                                                          The following example shows a simple has-a relationship between a car object and its engine object; it uses normal composition, where the Engine member object appears in the private: section of the Car class.

                                                           class Engine {
                                                           public:
                                                             void publ() throw();
                                                           protected:
                                                             void prot() throw();
                                                           };
                                                           
                                                           class CarA {
                                                           public:
                                                             // ...
                                                           private:
                                                             Engine e_;
                                                           };
                                                           

                                                          Obviously composition does not create an is-a relationship: a CarA is not a kind-of an Engine. In particular, users cannot legally convert a CarA* to an Engine*. Also note that a CarA object contains exactly one Engine object (though it could be made to contain more than one).

                                                          Private inheritance accomplishes essentially the same thing. In the following example, CarB is said to privately inherit from Engine (if the symbols : private Engine are changed to : protected Engine, CarB is said to protectedly inherit from Engine).

                                                           class CarB : private Engine {
                                                           public:
                                                             // ...
                                                           };
                                                           

                                                          Just as with normal composition, there is no is-a relationship: a CarB is not a kind-of Engine. In particular, normal (nonfriend) users cannot legally convert a CarB* to an Engine*. Also like normal composition, a CarB object contains exactly one Engine object (however, unlike normal composition, private and protected inheritance does not allow a second Engine subobject to appear as a second private and/or protected base class).

                                                          The main difference between composition and private/protected inheritance is access to the protected members of Engine. With private/protected inheritance, members and friends of CarB can access the protected: members of Engine (in this case, they can access both Engine::publ() and Engine::prot()). However, with normal composition, members and friends of CarA can only access Engine::publ(); they are forbidden to access Engine::prot(). The usual reason people use private/protected inheritance is for this additional access authority; but note that the extra authority carries with it extra responsibility.

                                                          Another difference between composition and private/protected inheritance is the ability to convert a CarB* to an Engine*. With private/protected inheritance, members and friends of CarB can convert a CarB* to an Engine*. With normal composition this is not possible: no one can legally convert a CarA* to an Engine*.

                                                          There are several caveats when using private/protected inheritance. Simple composition (has-a) is needed if it is necessary to contain several member objects of the same class; private or protected inheritance can introduce unnecessary multiple inheritance.

                                                            FAQ 37.04 When should normal has-a be used, rather than private or protected inheritance?

                                                            Use normal has-a when you can; use private or protected inheritance when you have to.

                                                            Normal composition (has-a) is preferred because it leads to fewer dependencies between classes. Private and protected inheritance are more expensive to maintain because they increase the number of classes that have access to the protected parts of other classesthey increase coupling.

                                                            Private or protected inheritance is often used when the goal is has-a but the interface of the contained class is insufficient. In this case, an alternative to private or protected inheritance is improving the public interface of the base class so that simple composition can be used. If you cannot change the interface of the base class (for example, because the source code is not available), you can create one derived class (often using public inheritance) that has an improved interface. This derived class with its improved interface is then used via simple composition (has-a).

                                                              FAQ 37.05 What are the access rules for public, protected, and private inheritance?

                                                              In the following example, class B has a public: member, a protected: member, and a private: member.

                                                               class B {
                                                               public:
                                                                 void publ() throw();
                                                               protected:
                                                                 void prot() throw();
                                                               private:
                                                                 void priv() throw();
                                                               };
                                                               

                                                              Class PrivD privately inherits from B, class ProtD protectedly inherits from B, and PublD publicly inherits from B.

                                                               class PrivD : private   B { };
                                                               class ProtD : protected B { };
                                                               class PublD : public    B { };
                                                               

                                                              With private inheritance, the public and protected parts of B become private in PrivD. This means that PrivD can access these member functions, but user code and classes derived from PrivD cannot access them.

                                                              With protected inheritance, the public and protected parts of B become protected in ProtD. This means that members and friends of ProtD can access these member functions, as can classes derived from ProtD, but user code cannot access them.

                                                              With public inheritance, the public parts of B become public on PublD, and the protected parts of B remain protected in PublD.

                                                              In all three cases, the private parts of B are inaccessible to the derived classes (PrivD, ProtD, and PublD) as well as to user code.

                                                                FAQ 37.06 In a private or protected derived class, how can a member function that was public in the base class be made public in the derived class?

                                                                The name (not the entire signature) of the member function should be declared in the public interface of the derived class preceded by the keyword using. For example, to make the member function B::f(int,char,float) public in PrivD, say this:

                                                                 class B {
                                                                 public:
                                                                   int f(int, char, float) throw();
                                                                 protected:
                                                                   int g(double, char) throw();
                                                                 };
                                                                 
                                                                 class PrivD : private B {
                                                                 public:
                                                                   using B::f;                                        <-- 1
                                                                 };
                                                                 

                                                                (1) Note: Omit the parameter declarations

                                                                The syntax for doing this with protected inheritance is identical.

                                                                There are two limitations to this technique: overloaded names can't be distinguished, and a base member cannot be made public if it was protected in the base class (that is, this technique cannot be used to make Base::g(double,char) public in the derived class). When necessary, both these limitations can be avoided by defining a call-through member function in the privately/protectedly derived class, as shown in the following example.

                                                                 class PrivD2 : private B {
                                                                 public:
                                                                   int g(double d, char c) throw();
                                                                 };
                                                                 
                                                                 inline int PrivD2::g(double d, char c) throw()
                                                                 { return B::g(d, c); }
                                                                 

                                                                  FAQ 37.07 Should a pointer be cast from a private or protected derived class to its base class?

                                                                  No.

                                                                  Within a member function or friend function of a private or protected derived class, the relationship to the base class is known and the upward pointer or reference conversion takes place automatically without a cast.

                                                                  In normal user code, the relationship to a private or protected base class is inaccessible and the conversion is illegal. Users should not perform a cast because private or protected inheritance is a nonpublic decision of the derived class. The cast will subtly break at some future date if/when the private or protected derived class chooses to change or remove the private/protected inheritance relationship.

                                                                  The conclusion is that only a class and its friends have the right to convert a pointer to the class's nonpublic base class. The member functions and friend functions of the privately/protectedly derived class don't need a cast because the relationship with the base class is directly accessible to them.

                                                                  Here's an even simpler conclusiondon't use pointer casts unless there is an overriding reason to do so!

                                                                    FAQ 38.01 What is the type of a pointer to a nonstatic member function?

                                                                    The most important thing to understand is that the type is different from that of a pointer to a C-style (non-member) function. Simply understanding that they are completely different and have incompatible types will prevent the most common and dangerous errors with pointers to member functions.

                                                                    A pointer to the nonstatic member function with signature void Fred::f(int) has type void(Fred::*)(int). In particular, the type of the pointer to a nonstatic member function includes the class of the member function because nonstatic member functions have an implicit parameter that points to the object (the this pointer).

                                                                    Here's an example.

                                                                     #include <iostream>
                                                                     using namespace std;
                                                                     
                                                                     class Fred {
                                                                     public:
                                                                       void f(int i) throw();
                                                                       void g(int i) throw();
                                                                       void h(int i) throw();
                                                                     };
                                                                     
                                                                     void Fred::f(int i) throw()
                                                                       { cout << "Fred::f(int); i=" << i << '\n'; }
                                                                     void Fred::g(int i) throw()
                                                                       { cout << "Fred::g(int); i=" << i << '\n'; }
                                                                     void Fred::h(int i) throw()
                                                                       { cout << "Fred::h(int); i=" << i << '\n'; }
                                                                     
                                                                     typedef void(Fred::*FredMemberPtr)(int);
                                                                     

                                                                    Note the use of the typedef. Because of the rather obscure syntax of pointers to nonstatic member functions, it is highly recommended that a typedef be used to represent the pointer type.

                                                                    In the following example, a pointer p is created to point to Fred::g. This pointer is then used to call the member function.

                                                                     void sample(Fred& x, FredMemberPtr p) throw()
                                                                     { (x.*p)(42); }                                      <-- 1
                                                                     
                                                                     int main()
                                                                     {
                                                                       FredMemberPtr p = &Fred::g;
                                                                       Fred x;
                                                                       sample(x, p);
                                                                     }
                                                                     

                                                                    (1) If p is &Fred::g, this is the same as x.g(42)

                                                                    The output of this program is as follows.

                                                                     Fred::g(int); i=42
                                                                     

                                                                    A pointer to a nonstatic member function of class Fred has a totally different type from a pointer to a function. For example, the pointer type void(Fred::*)(int) is totally different from the pointer type void(*)(int). Do not use a cast to try to convert between the two types. You have been warned.

                                                                    A pointer to a static member function of class Fred has the same type as a pointer to a C-like function. In other words, a C-like function or static member function can be converted to the same pointer to function type, such as void(*)(int). But a pointer to a nonstatic member function cannot be converted to a normal pointer to a function type.

                                                                      FAQ 38.02 Can pointers to nonstatic member functions be passed to signal handlers, X event call-back handlers, and so on, that expect C-like function pointers?

                                                                      A pointer to a nonstatic member function cannot be passed into a routine that is expecting a pointer to a C-like function, since a nonstatic member function is meaningless without there being an object to which the nonstatic member function can be applied.

                                                                      To simulate this behavior, pass a pointer to a C-like function, and have that function obtain the object pointer through some other technique (such as storing it in a global). The C-like function would then call the desired nonstatic member function. For example, suppose x.f(int) were to be called on interrupt, where f(int) is a nonstatic member function of the class of object x. The following would accomplish the call (note that a static member function has the same type as a C-like function).

                                                                       #include <iostream>
                                                                       #include <signal.h>
                                                                       using namespace std;
                                                                       
                                                                       class Fred {
                                                                       public:
                                                                         void f(int n) throw();
                                                                         static void staticMethod(int n) throw();
                                                                         static void registerHandlerObject(Fred* p) throw();
                                                                       private:
                                                                         static Fred* signalHandlerObject_;     //the handler object
                                                                       };
                                                                       
                                                                       void Fred::f(int n)
                                                                         { cout << "Fred::f()\n"; }
                                                                       void Fred::registerHandlerObject(Fred* p)
                                                                         { signalHandlerObject_ = p; }
                                                                       void Fred::staticMethod(int n)
                                                                         {
                                                                           Fred* p = Fred::signalHandlerObject_;
                                                                           p->f(n);
                                                                         }
                                                                       Fred* Fred::signalHandlerObject_ = NULL;
                                                                       
                                                                       int main()
                                                                       {
                                                                         //signal(SIGINT, Fred::f);                         <-- 1
                                                                         Fred x;
                                                                         Fred::registerHandlerObject(&x);
                                                                         signal(SIGINT, Fred::staticMethod);                <-- 2
                                                                       }
                                                                       

                                                                      (1) ERROR: Cannot do this

                                                                      (2) Good

                                                                        FAQ 38.03 What is one of the most common errors when using pointers to member functions?

                                                                        Trying to pass the address of a nonstatic member function into a function that is expecting a pointer to function, and sometimes the inverse of this scenario.

                                                                        Nonstatic member functions have an implicit parameter that points to the objectthe pointer called this inside the member function. Nonstatic member functions can be thought of as having a different calling convention from that of normal C functions, so the types of their pointers are different and incompatiblepointer to nonstatic member function versus pointer to function.

                                                                        C++ introduces a new type of pointer, called a pointer to nonstatic member, which can be invoked only by providing an object. Do not attempt to cast a pointer that points to a nonstatic member function into a pointer to function or vice versa; the result is undefined and probably disastrous. For example, a pointer to nonstatic member function probably doesn't contain the machine address of the appropriate function. As noted in the last example, if a regular C function pointer is needed, use either a static member function or a nonmember function.

                                                                          FAQ 38.04 How is an array of pointers to nonstatic member functions declared?

                                                                          Use a typedef.

                                                                          Consider the following class example.

                                                                           #include <iostream>
                                                                           using namespace std;
                                                                           class Fred {
                                                                           public:
                                                                             void f(int i);
                                                                             void g(int i);
                                                                             void h(int i);
                                                                           };
                                                                           
                                                                           void Fred::f(int i)
                                                                             { cout << "Fred::f(int); i=" << i << '\n'; }
                                                                           void Fred::g(int i)
                                                                             { cout << "Fred::g(int); i=" << i << '\n'; }
                                                                           void Fred::h(int i)
                                                                             { cout << "Fred::h(int); i=" << i << '\n'; }
                                                                           
                                                                           typedef void (Fred::*FredMemberPtr)(int);
                                                                           

                                                                          Since FredMemberPtr is a typedef, it can be used like most other data types. In particular, an array of FredMemberPtr can be created using the following syntax.

                                                                           FredMemberPtr array[3] = { &Fred::f, &Fred::g, &Fred::h };
                                                                           

                                                                          To call one of the nonstatic member functions, supply a Fred object, and use the .* operator.

                                                                           int main()
                                                                           {
                                                                             Fred x;
                                                                             for (int i = 0; i < 3; ++i) {
                                                                               FredMemberPtr p = array[i];
                                                                               (x.*p)(42 + i);
                                                                             }
                                                                           }
                                                                           

                                                                          The output of this program is as follows.

                                                                           Fred::f(int); i=42
                                                                           Fred::g(int); i=43
                                                                           Fred::h(int); i=44
                                                                           
                                                                            Оставьте свой комментарий !

                                                                            Ваше имя:
                                                                            Комментарий:
                                                                            Оба поля являются обязательными

                                                                             Автор  Комментарий к данной статье