⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch14.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<tt>  int secs;</tt><tt>  int minutes;</tt><tt>  int hours;</tt><tt>public:</tt><tt>  //...</tt><tt>  DateTime::DateTime(ifstream&amp; archive); //reconstituting constructor</tt><tt>  ofstream&amp; Write(ofstream&amp; archive);</tt><tt>  ifstream&amp; Read(ifstream&amp;  archive);</tt><tt>};</tt><tt>ofstream&amp;  DateTime::Write(ofstream&amp; archive)</tt><tt>{</tt><tt>  Date::Write(archive); //must invoke base class Write() first</tt><tt>  archive.write( reinterpret_cast&lt;char*&gt; (&amp;), sizeof(day));</tt><tt>  archive.write( reinterpret_cast&lt;char*&gt; (&amp;month), sizeof(month));</tt><tt>  archive.write( reinterpret_cast&lt;char*&gt; (&amp;month), sizeof(year));</tt><tt>  return archive;</tt><tt>}</tt><tt>ifstream&amp;  DateTime::Read(ifstream&amp; archive)</tt><tt>{</tt><tt>  Date::Read(archive);</tt><tt>  archive.read( reinterpret_cast&lt;char*&gt; (&amp;day), sizeof(day));</tt><tt>  archive.read( reinterpret_cast&lt;char*&gt; (&amp;month), sizeof(month));</tt><tt>  archive.read( reinterpret_cast&lt;char*&gt; (&amp;month), sizeof(year));</tt><tt>  return archive;</tt><tt>}</tt><tt>DateTime::DateTime(ifstream&amp; archive) //reconstituting constructor</tt><tt>{</tt><tt>  Read(arcive);</tt><tt>}</tt></pre><h4> Third Party Classes</h4><p>Overriding the member functions <tt>Read()</tt> and <tt>Write()</tt> and serializing   data members to and from a stream are error prone and can cause maintenance   difficulties. Whenever data members are added or removed, or when their types   are changed, the implementer has to modify these member functions accordingly   -- but this is still managable. However, deriving from classes that do not define   a reconstituting constructor and the member functions <tt>Read()</tt> and <tt>Write()</tt>   is more difficult to handle because a derived class can only serialize its own   members -- not members of its base classes. The same difficulties exist with   embedded objects. How are such subobjects serialized? It might be possible to   overcome these difficulties in some cases, albeit with considerable efforts.   For example, a class that contains a <tt>vector</tt> can iterate through the   vector's members and serialize them one by one. This is only half the story,   though. A vector's state depends on other parameters, such as its capacity.   Where can this information be stored if the <tt>vector</tt> object itself cannot   be serialized? Serializing arrays is another conundrum. One solution is to write   a header in the beginning of every serialized object that contains the number   of elements. However, this won't work with reference counted objects. Most implementations   of <tt>std::string</tt> are reference counted, which means that in the following   code snippet, the five <tt>string</tt> objects share some of their data members:</p><pre><tt>#include &lt;string&gt;</tt><tt>using namespace std;</tt><tt>void single_string()</tt><tt>{</tt><tt>  string sarr[4];</tt><tt>  string s = sarr[0];</tt><tt>  for (int i = 1; i&lt; 4; i++)</tt><tt>  {</tt><tt>    sarr[i] = s;</tt><tt>  }</tt><tt>}</tt></pre><p>Reference counting is an implementation detail that is hidden from the users   of the class; it is impossible to query the <tt>string</tt> object about how   many strings it represents and to serialize this datum.</p><p>Handmade object persistence is becoming much more complicated than it seemed   at first, isn't it? But that's not all yet. How might such a handmade persistence   model represent templates? By simply storing specializations as ordinary objects,   the model fails to represent the relationship that exists among the specializations.   Worse yet, multiple inheritance and virtual inheritance are even more challenging.   How can a handmade persistence model ensure that a virtual subobject is serialized   only once, regardless of the number of its occurrences in the inheritance graph? </p><h4> A Proposal for a Standardized Persistence Model</h4><p>Most programmers probably give in at this point, and rightfully so. It is possible   to come up with a solution even to the virtual base class problem, but as soon   as this problem is solved, other special cases such as function objects, static   data members, reference variables, and unions present more complexities. There   is another drawback in the handmade persistence model: It is not standardized,   and as such, programmers have to implement it on their own. The result is a   lack of uniformity and varying levels of reliability and performance. Without   standardized support for object persistence, a homemade persistence model is,   at best, brittle and error prone. Obviously, without standardized object persistence   it is impossible to ensure simple, portable, and efficient serialization and   deserialization of objects. </p><h4> Library-Based Extensions</h4><p>What might such a standardized persistence model look like? There are two basic   strategies. One is library-based, whereas the other relies on core language   extensions (keywords and syntax). A library-based solution is advantageous in   many respects. For example, it does not extend the core language, thus avoiding   additional burden for programmers who do not intend to use persistent objects.   In addition, a library can be replaced by a better implementation from another   vendor without having to switch to a different compiler. This practice can be   seen today with people who uninstall the original STL implementation -- provided   by the compiler vendor -- and replace it with another one. Still, a library-based   solution has to deal with the lack of language support for persistence, and   it must face the same difficulties and complications that were demonstrated   previously (the intricacies and vagaries of the most widely used object distribution   frameworks, namely the Distributed Component Object Model (DCOM) and the Common   Object Request Broker Architecture (CORBA), prove this point). STL might have   never become what it is today without built-in support for templates and operator   overloading. Furthermore, the language support for templates was extended in   various ways to provide the necessary constructs for STL (see Chapter 2, "Standard   Briefing: The Latest Addenda to ANSI/ISO C++,"<i> </i>and Chapter 9). Similarly,   the support for persistence requires core language extensions.</p><h4> A New Constructor Type</h4><p>The special member functions are automatically synthesized by the implementation   if the programmer does not declare them explicitly and if the implementation   needs them (see Chapter 4). Similarly, a language extension can be made so that   another type of constructor, a <i>reconstituting constructor</i>, is either   implicitly synthesized by the implementation when needed, or so that it can   be declared by the programmer. As is the case with other constructor types,   the programmers need to be allowed to override the default reconstituting constructor   by defining it explicitly. The syntactic form of such a constructor must be   distinct from all other constructor forms. In particular, a reconstituting constructor   is not to be identified solely by its signature. In other words, the following</p><pre><tt>class A</tt><tt>{</tt><tt>//...</tt><tt>public:</tt><tt>  A(istream&amp; repository ); //reconstituting ctor or an ordinary constructor</tt><tt>};</tt></pre><p>is not recommended. It might well be the case that the programmer's intention   was to define an ordinary constructor that takes an <tt>istream</tt> object   by reference and not a reconstituting constructor. Furthermore, such a convention   might break existing code. A better approach is to add a syntactic clue that   signifies a reconstituting constructor exclusively. For example, by preceding   the symbol <tt>&gt;&lt;</tt> to the constructor's name</p><pre><tt>class A</tt><tt>{</tt><tt>//...</tt><tt>public:</tt><tt>  &gt;&lt;A(istream&amp; repository ); //reconstituting constructor</tt><tt>};</tt></pre><p>the reconstituting constructor can take a single parameter of some stream type.   This parameter is optional. When the reconstituting constructor is invoked without   an argument, the implementation deserializes the object from a default input   stream that can be specified in the compiler's setting (similar to the default   location of the standard header files). To automate the serialization process,   a serializing destructor is also necessary. How might such a destructor be declared?   One solution is to add another type of destructor so that classes can have two   destructor types. This is, however, troublesome because the object model of   C++ is based on a single destructor per class. Adding another type of destructor   is ruled out then. Perhaps there is no need to define a distinct destructor   type. Instead, the existing destructor can do the serialization automatically:   The compiler can insert into the destructor additional code that performs the   necessary serialization operations. (As you know, compilers already insert code   into user-defined destructors to invoke the destructors of base classes and   embedded objects.) </p><p>Automating the serialization process has drawbacks, too. Not every class has   to be serialized. The overhead of serializing an object should be imposed only   when the user really needs it. Furthermore, the possibility of encountering   runtime exceptions during serialization is rather high. A full hard disk, a   broken network connection, and a corrupted repository are only a handful of   the possible runtime exceptions that can occur during the process of writing   the contents of an object to a permanent storage medium. However, throwing an   exception from a destructor is highly undesirable (see Chapter 6), so perhaps   automatic serialization during object destruction is too risky. Apparently,   there is no escape from explicitly calling a member function to do the job.   There are other obstacles here: How to handle the creation and serialization   of an array of objects? How to synchronize changes in the definition of a class   and the contents of an object that was serialized before the change took place?   Every language that supports object persistence deals with these difficulties   in its own way. C++ can borrow some of these ideas, too, or it can initiate   innovative ideas.</p><p>This discussion gives you some feel of why language extensions are necessary,   and what kind of obstacles they overcome. However hypothetical this discussion   might seem, the evolution of C++ has been a democratic process. Many of the   changes and extensions were initiated by users of the language rather than Standardization   committee members. STL is probably the best example of this. If you have a comprehensive   proposal for such an extension, you can present it to the Standardization committee.</p><h3> <a name="Heading12">Support for Concurrency</a></h3><p><i>Concurrency</i> is a generic term for multithreading and multiprocessing.   Concurrent programming can effectively improve performance and responsiveness   of an application, be it a word processor or a satellite homing system. C++   does not directly address the issues of multiprocessing, threads, and thread   safety. It is important to note, however, that nothing in the Standard Library   or the language itself <i>disallows </i>concurrency. Look at the example of   exception handling: In a multithreaded environment, exception handling should   be thread-safe, but a single-threaded environment can implement exception handling   in a non-thread-safe manner; this is an implementation-dependent issue. Implementations   are allowed to provide the necessary facilities for concurrency, and indeed   many of them do so. Again, without direct support from the programming language,   either by standardized libraries or by core extensions, the implementation of   thread safety is more complicated and highly nonportable. There have been several   proposals in the past for adding concurrency to C and C++. At present, however,   none of these languages supports concurrency directly. </p><h4> Multithreading</h4><p><i>Multithreading</i>, as opposed to multiprocessing, refers to the use of   several control threads in a single process. Multithreading is therefore simpler   to design and implement, and it enables the application to use system resources   more effectively.</p><p>Because all threads in a process share the process's data, it is essential   to synchronize their operation properly so that one thread does not interfere   with another. For that purpose, <i>synchronization objects</i> are used. Various   types of synchronization objects, such as mutex, critical section, lock, and   semaphore offer different levels of resource allocation and protection. Unfortunately,   the details and the characterizations of synchronization objects vary from platform   to platform. A standard library of synchronization objects has to be flexible   enough to enable users to combine platform-specific synchronization objects   with standard objects. This is similar to the use of <tt>std::string</tt> and   nonstandard string objects in the same program. Alternatively, the standard   thread library could provide the basic interfaces of the synchronization objects,   and the implementation would be platform-dependent. There is a problem with   introducing multithreading support into the Standard, however: single-threaded   operating systems such as DOS. Although these platforms are not very popular   these days, they are still in use, and implementing a thread library on these   platforms is nearly impossible.</p><h4> Thread safety</h4><p>Perhaps the Standard can provide only the necessary features for thread safety   and leave the other issues -- such as synchronization objects, event objects,   instantiation, destruction of threads, and so on -- implementation-defined,   as they are today. Thread safety ensures that an object can be used safely in   a multithreaded environment. For example, the following thread-unsafe class</p><pre><tt>class Date</tt><tt>{</tt><tt>private:</tt><tt>  int day;</tt><tt>  int month;</tt><tt>  int year;</tt><tt>public:</tt><tt>  Date(); //current date</tt><tt>  ~Date();</tt><tt>     //accessors</tt><tt>   int getDay() const { return day; }</tt><tt>   int getMonth() const { return month; }</tt><tt>   int getYear() const { return year; }</tt><tt>    //mutators</tt><tt>   void setDay(int d)  { day = d; }</tt><tt>   void setMonth(int m)  { month = m; }</tt><tt>   void setYear(int y)  { year = y; }</tt><tt>};</tt></pre><p>can become thread-safe by applying the following changes to it: At the beginning   of each member function, a lock has to be acquired; in every return point of   each member function, the lock has to be released.</p><p>The modified member functions now look like this:</p><pre><tt>void Date::setDay(int d)</tt><tt>{</tt><tt>  get_lock();</tt><tt>  day = d;</tt><tt>  release_lock();</tt>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -