Chapter 15 Division

xiaoxiao2021-03-06  15

Chapter 15 Division

This chapter explains how to generate scalable programs.

This chapter contains some topics:

* Detective class review

* Multi-base class

* Virtual function

* Abstract class

* Summary of Range Rules

Detective class outline

With the inheritance mechanism, new classes can be born from existing classes (related to inheritance see the beginning "single inheritance"). Those classes for derived are called "base classes" of these specially born classes. Detective classes can be used with the following grammar.

grammar

Base class description:

Base class

Basic class table:

Base class specifier

Basis table, base class specifier

Base class specifier:

Full class name

Virtual Access Configuration Opt Complete Class Name

Access indicator VirtualOpt complete class name

Access indicator:

Private

protected

public

Single inheritance

In the most common form of "single inheritance", the derived class has only one base class, considering the relationship shown in Figure 9.1.

Note that from the general to special procedures in Figure 9.1. In the hierarchical design of the class, some universal characteristics can be found, that is, the classic class is always the "Kind of" relationship with the base class. In Figure 9.1, a book is a kind of printing document and a book is a book.

Another worth noticing in Figure 9.1 is that Book is both derived (derived from PrintedDocument), and is a base class (PaperBackbook is derived from Book). The following example is a profile of this level.

Class PrintedDocument

{

// member table

}

// book is derived from PrintedDocument

Class book: Public PrintedDocument

{

// member table

}

// PaperbackBook is derived from the BOOK

Class PaperbackBook: Public Book

{

// member table

}

PrintedDocument as the direct base class of the Book, which is also the non-direct base class of PaperbackBook. The difference between the direct base classes and the non-direct base class is that the direct base class appears in the base class table of class description, and the direct base class does not appear in the base class.

The description of each derived class is illustrated after the specification of the base class, so it is not enough for the base class only to give only one forward reference, must be a complete description.

In the previous example, the access instructions used are public. The inheritance of public inheritance, private inheritance, and protection is described in Chapter 10, "Members Access Control".

A class can be used as a base class, as shown in Figure 9.2.

The icon in Figure 9.2 is called "a to ringless map" (DAG). Some classes are the base class of multiple derived classes. But in turn is not true: there is only one direct base class for any given derived class. Figure 9.2 depicts a single inheritance structure.

Note: The aircly no looping is not only used for single inheritance. They can also be used for multiple inheritance diagrams. This topic will be described in the "multiple inheritance" in the next section.

In the inheritance, the derived class contains a base class plus any new member. As a result, derived classes can reference the base class (unless these members are defined in the derived class). These members can be referenced using the scope dislikes (: :) references these members when they are relied in a member of the derived class or an indirect base class. Consider the following code:

Class Document

{

PUBLIC:

Char * name; // Document Name

void printnameof (); // print name

}

/ / Implement the PrintNameOF function of class Document

Void Document :: PrintNameOf ()

{

Cout << name << End;

}

Class book: Public Document

{

PUBLIC:

Book (Char * Name, long pagecount);

Private:

Long PageCount;

}

// Class Book Construction Function

Book :: Book (Char * Name, long pagecount)

{

Name = MEW Char [Strlen 1];

STRCPY (Name, NAME);

PageCount = PageCount;

}

Note that the Book constructor (book :: book) has access to the data member name. You can create a Book class object in the program and use it.

// Create a new object of a book class, which will activate the constructor book: BookbookLibraryBook ("Programming Windows, 2nd Ed", 994);

...

// Use the function printnameof inherited from the document.

Librarybook.printNameOf (); as shown in the previous example, class members and inherited data and functions are referenced in a consistent manner. If the printnameOf called by class Book is implemented by class BOOK redefine, the PrintNameOF function that belongs to class Document can only use the range of distress (: :) to use:

Class book: Public Document

{

Book (Char * Name, long pagecount);

Void PrintNameOf ();

Long PageCount;

}

Void book :: PrintNameOf ()

{

Cout << "Name of Book:";

Document :: PrintNameOf ();

}

As long as there is an accessible, unlawful base class, the derived pointer and reference can imply the pointers and references to their base classes. The following example confirms the concept of this use pointer (also applies to references):

#include

void main ()

{

Document * doclib [10]; // 10 libraries of the document

For (int i = 0; i <10; i)

{

COUT << "Type of Document:"

<< "P) Aperback, M) Agazine, H) ELP File, C) BT"

<< ENDL;

Char cdoctype;

CIN >> CDOCTYPE;

Switch (tolower (cdoctype))

{

Case 'P':

Doclib [i] = new paperbackbook;

Break;

Case 'M':

Doclib [i] = new magazine;

Break;

Case 'h':

Doclib [i] = new helpfile;

Break;

Case 'C':

Doclib [i] = new computerbasedtraining;

Break;

DEFAULT:

i;

Break;

}

}

For (i = 0; i <10; i)

Doclib [I] -> PrintNameOf ();

}

In the Switch statement of the previous example, different types of objects have been created. This depends on the instructions made to the CDOCTYPE object. However, these types are derived from class Document, so they can be implied to Document *. The result is DOCLIB to become a "HETEROGENEOUS LIST). This linked list contains different types of objects, all objects do not have the same type.

Because the Document class has a printNameOf function. Therefore, it can print the name of each book in the library, but some information is omitted for the Document type (such as: Book's total number of pages, number of bytes of HelpFile, etc.).

Note: Forced base classes to implement a function such as PrintNameOf, usually not a good design, and a replaceable design method is provided in the "virtual function" behind this chapter.

Multiple inheritance

Some versions of the later period of C introduced the "multiple inheritance" mode. In a multi-inheritance figure, the derived class can have multiple direct base classes. Consider Figure 9.3.

In the figure shown in 9.3, a CollectibleString class is displayed. This class is just like a Collectible class (a class that can be inconclusive), as a String class. For derived classes require multiple base classes, multiple inheritance is a good solution. Therefore, it is also easy to send CollectibleCustomer and CollectibleWindow, and more. For a specific program If the properties of each class are not all required, each class can be used separately or in the same class. Therefore, the class level depicted in Figure 9.3 is used as the basis, and the user can easily organize uncommitted strings or collectible non-strings. Without the use of single inheritance, there is no such convenience.

Deficky class level

There are some types of levels very huge, but there are many things that are very common. These universal code are implemented in the base class, but special code is realized in the derived class.

It is important to establish a mechanism for the base class, and the class can complete a large number of function functions through this mechanism.

This mechanism is usually implemented with virtual functions. Sometimes, based on these functions provide a default implementation. As in Figure 9.2's Document class level, two important functions are Identify and WHEREIS. When calling the Identify function, return a correct identifier. For a variety of documents: For Book, the function that calls if DOC-> Identify () must return to the ISBN number; and more reasonable for a HelpFile returns a product name and version number. Similarly, whereis functions should return rows and bookshelf numbers for a book, but for helpfile, it should return its disk location, perhaps a directory and name.

It is important to find out that all Identify and WHEREIS functions return is the same type of information. In this example, it is exactly a descriptive string.

These functions can be implemented as a virtual function, and then call with pointers to the base class, which will be determined at runtime for the actual codes to select the correct Identify and Whereis functions.

Implementation of class protocol

Classs can be implemented to force certain protocols. These classes are called "abstract classes" because objects cannot be created for this type. They are only existed to derive other classes.

This class is referred to as an abstract class when a category contains a pure virtual function or when they inherit some of the pure virtual functions. The pure virtual function is a virtual function defined by a pure set. as follows:

Virtual char * identify () = 0;

The base class Document imposes some protocols to the derived class.

* Provide a suitable implementation for the Identify function

* Provide a suitable implementation for whereis functions

When designing the Document class, the class designer can ensure that non-abstract classes cannot be achieved if the Identify and WHEREIS functions are not provided. Therefore, the Document class contains the following description:

Class Document

{

PUBLIC:

...

// For the requirements of the school, they must implement the following functions

Virtual char * identify () = 0;

Virtual char * whereis () = 0;

...

}

Base class

As previously discussed, new derivatives created by inheritance processes are made of members of the base class plus new members of the derived class. In multiple inheritance, a hierarchical diagram can be constructed, in which the same base class can be part of a plurality of derived classes. Figure 9.4 shows this picture.

The composition of CollectibleString and CollectibleSortable is expressed in Figure 9.4. However, the base class Collectible passes the path CollectibleSortable and CollectibleString to class CollectibleSortableSrtableString. In order to eliminate this redundancy, when these classes are inherited, they can be explained as the virtual base class.

See how virtual base classes and objects with virtual base classes are made, see "virtual base classes" behind this chapter.

Multiple base class

As described in multiple inheritance, a class can be born from multiple base classes. In the multi-inheritance mode derived from multiple base classes, the base class is illustrated by the base class indicator (see the syntax "in this chapter). For example, the CollectionOfbook class is derived from class Collection and Book classes, which can be described as follows:

Class CollectionOfbook: Public Book, Public Collection

{

//new member

}

The description of the base class generally does not have an important meaning unless the constructor and the destructor are called in some cases. In these cases, the description order of the base class will have an impact on the following. * The order of initialization caused by the constructor. This explanation will be important if your code depends on the Book section of CollectionOfbook to initialize before the Collection section. Initialization is initialized in the description order in the base class table.

* Activate the destructuring function to make the order of clearance. Similarly, when other parts are being cleared, this order is also important if some special parts are retained. The call is called in the reverse direction of the order of the base class table.

Note: The order of the base class will affect the memory distribution of the class. Do not make any programming decisions on the order of the base class in the memory.

When you explain the base class, you cannot explain the same name. However, for a derived class, its non-direct base category can have multiple identical.

Virtual base class

Because a class can be used as a non-direct base class for a derived class. C provides a way to optimize this base class. Research on the class level in Figure 9.5, which shows an simulated lunch line.

In Figure 9.5, Queue is the base class of CashierQueue and LunchQueue. However, when these two classes combine together to form LunchcashierQueue, the following issues are generated: new classes contain sub-objects with two queue types, one derived from CachierQueue, and another from LunchQueue. Figure 9.6 shows a conceptual memory distribution (the actual content announcement may be optimized).

Note that in the LunchcashierQueue object, there are two Queue sub-objects. The following code description Queue is a virtual base class:

Class queue

{

// member table

}

Class CashierQueue: Virtual Public Queue

{

// member table

}

Class Lunchqueue: Virtual Public Queue

{

// member table

}

Class LunchcashierQueue: Public LunchQueue, Public CashierQueue, PUBLIC

{

// member table

}

Keyword Virtual ensures copy of only one Queue object (see Figure 9.7).

A class can have virtual components for a given type, or there may be non-virtual components. This happens in the case of Figure 9.8.

In Figure 9.8, CACHIERQUEUE and LUNCHQUEUE use Queue as a virtual base class. But TakeoutQueue only describes Queue as base classes, not a virtual base class. So LunchTakeoutcashierQueue has two queue sub-objects:

One is inherited from the path including LunchcashierQueue, and the other is from the path to TakeoutQueue, and Figure 9.9 shows this.

Note: Virtual inheritance has the advantage of size compared to non-virtual inheritance, but it also introduces additional operation overhead.

If a derived class overloads a virtual function inherited from the virtual base class, and the derived class is called to point to the constructor and the destructive function, the compiler will introduce an additional implicit "" VTORDISP "Domain into classes with virtual base classes. The / VD0 compiler option is forbidden to increase this implicit VTORDISP constructor. / VD1 option (default) so that it can be released when needed. The virtual function can be turned off only if you are confident that all the constructor or the destructor of all classes or destructor is virtual.

/ VD compiler option affects global compilation mode. Compiling instructions with VTORDISP can open or disable vtordisp domains on a class-based mode:

#pragma vtordisp (OFF)

Class GetReal: Virtual Public {...};

#pragma vtordisp (ON)

Name

Multiple inheritors make it possible to inherit the member name from different paths. The name of the member along these paths is not necessarily unique. Conflicts of these names are called "secondary".

Any reference to a reference member must use a non-sense reference. The following example shows how the second meaning occurs.

// Describe two base classes A and B

Class A

{

PUBLIC:

UNSIGNED A;

Unsigned b ();

}

Class B

{

Public: unsigned a (); // Note Class A also has a member "a" and a member "B"

INT B ();

Char C;

}

/ / Define the class C from the class A and class B

Class C: Public A, PUBLIC B

{

}

According to the class description given above, the following code will lead to an unsatisfaction, because it is unclear whether it is a reference to class A, or the class B:

C * pc = new C;

PC-> b ();

Consider the above code, because the name A is both a class A and a member of class B, so the compiler cannot distinguish from the function indicated by which A indicated by the end. Access a member if it can represent multiple functions, objects, types, or enumerations cause an unsatisfaction.

The compiler is performed in the order below to detect erriness:

1. If the name of the accesses are unsatisfactory (as described above), an error message is generated.

2. If the overload function is ignorant, they have no problems (see "Parametric Match" in Chapter 12, "Reserved" in Chapter 12, "Refills").

3. If the name of the accesses destroyed member access licenses, an error message is generated (see Chapter 10 "Members Access Control").

When an expression produces an unsurface generated by inheritance, the name of the problem can be manually resolved by using the name of the class name limit. To make the previous code correctly correctly compile, press as follows Use code:

C * pc = new C;

Pc-> b :: a ();

Note: After the class C description, reference B will potentially cause an error in the range of C. However, an error is generated until a reference to B unlimited reference is actually used in the range of C.

Elightening and virtual base class

If a virtual base class, a function, an object, a type, and an enumeration can be reached by multiple inheritance paths, but because only one virtual base class is used, it is not caused by accessing these names. Figure 9.10 shows the composition of the object using the virtual base class and the non-virtual basis.

In Figure 9.10, access to any class A is accessed by non-virtual base classes, will cause an unstriability; because the compiler does not have any information to explain the sub-objects that are related to the same class B, or contact with the same class A child object together, however, when a means a virtual base class, there is no problem with which one sub-objects accesses.

Dominate

There may be multiple names (functions, objects, enumerations) by inheritance. This situation is regarded as an amphibian caused by the non-virtual basis. However, virtual base classes can also cause erliness, unless a name "dominating" is other name. One

A name gives other names in the name definition in two classes, one of which is the name of another derived, dominated position is the name in the derived class. When this name is used, the opposite will not produce Embodiment, as shown in the following code:

Class A

{

PUBLIC:

Int a;

}

Class B: Public Virtual A

{

PUBLIC:

Int a ();

}

Class C: Public Virtual A

{

...

}

Class D: Public B, Public C

{

PUBLIC:

D () {a ();} // does not generate an unsurmount, b :: a () dominated A :: A

}

Conversion unsatisfaction

Explicitly or implicitly a pointer to the class type of the class or reference can also cause erliness. Figure 9.11 shows the following:

* Describes an object of type D.

* Use the address operator for this object. Note that the address operator always supports the base class address of the object.

* Explicitly convert the pointer obtained by the address operator to the pointer to the base class type A. Note that the address shape of the object is converted into type A *, and it is usually not provided to the compiler to provide sufficient information to determine which A type of child object is selected. In this case, there is a sub-object of two A types.

For the conversion of a *, there is an unity, because there is no way to distinguish which A-type sub-object is correct.

Note that as long as you want to use which child object you want to use, as follows:

(A *) (b *) & D // use b

(A *) (c *) & D // use C child object

Virtual function

The virtual function ensures that the correct function is called in an object, regardless of the expression of the function.

Suppose a base class contains a function of the same name for the virtual function and a derived class defines the same name. The function in the derived class is called by objects in derived class, even it can be called with pointers and references to the base class. The following example shows a base class to provide a PrintBalance function: Class Account

{

PUBLIC:

Account (Double D); // Constructor

Virtual double getBalance (); // get balance

Virtual void printbalance (); // Default implementation

Private:

Double _balance;

}

// Constructor ACCOUNT implementation

Double Account :: Account (Double D)

{

_balance = d;

}

// Account GetBalance

Double Account :: getBalance ()

{

Return_balance;

}

// PrintBalance's default implementation

Void Account :: PrintBalance ()

{

CERR << "Error.Balance Not Available for Base Type".

<< ENDL;

}

Two derived classes CheckingAccount and Savingsaccount are created as follows:

Class CheckingAccount: Public Account

{

Public: void

PRINTBALANCE ();

}

// CheckingAccount's PrintBalance 0

Void CheckingAccount :: PrintBalance ()

{

COUT << "Checking Account Balance:"

<< getBalance ();

}

Class Savingsaccount: Public Account

{

PUBLIC:

Void PrintBalance ();

}

SAVINGSACCOUNT

CoID Savingsaccout :: PrintBalance ()

{

Cout << "SAVINGS Account Balance:"

<< getBalance ();

}

Functions PrintBalance is virtual in derived class because it is in the base class account, which is description as virtual, to call the virtual function such as PrintBalance, you can use the following code:

// Create type checkingaccount and savingsaccount objects

CheckingAccount * PChecking = New CheckingAccount (100.00);

Savingsaccount * psavings = new savingsaccount (1000.00);

// Call PrintBalance with pointer to Account

Account * paccount = pchecking;

Paccount-> PrintBalance ();

// Use pointer to Account to call PrintBalance

Paccount = PSAVINGS;

Paccount-> PrintBalance ();

In the previous code, in addition to the object referred to in PACCount, the code called PrintBalance is the same.

Because PrintBalance is a virtual, it will call the function version defined by each object, and the function "overwrites" the same name function in the base class in the derived class checkingaccount and savingsaccount. If there is no implementation of a class's instructions, the default implementation in the base class Account will be used.

The function in the derived class reloads the virtual function in the base class, only the same time only in their type. Functions in derived classes cannot be different in the same field in the return value; the parameter table must also be different. When a pointer or reference call is called, you must follow the following rules: * Interpretation of the virtual function call depends on the type based on the object called their objects.

* Interpretation of non-virtual function call depends on the type of pointer or reference to calling them.

The following example shows their behavior when using pointer calls virtual or non-virtual functions: #include

// Describe a base class

Class Base

{

PUBLIC:

Virtual void nameOf (); // virtual function

Void invokingclass (); // non-virtual function

}

// Implementation of two functions

Void Base :: nameof ()

{

Cout << "Base :: NameOf / N";

}

Void Base :: InvokingClass ()

{

Cout << "Invoked by Base / N";

}

// Description a derived class

Class Derived: Public Base

{

PUBLIC:

Void nameOf (); // virtual function

Void invokingclass (); // non-virtual function

}

// Implementation of two functions

Void Derived :: Nameof ()

{

Cout << "Derived :: NameOf / N";

}

Void Derived :: invokingclass ()

{

COUT << "Invoked by Derived / N";

}

void main ()

{

// Describe an object of a deerid type

Derived aderived;

// Describe two pointers, one is Derived * type, the other is Base *, and initializes them with // aderived.

Derived * pderived = & aderived;

Base * PBase = & aderived;

// Call this function

PBASE-> nameOf (); // Call the virtual function

PBase-> invokingclass (); // call non-virtual functions

Pderived-> nameOf (); // Call the virtual function

Pderived-> invokingclass (); // call non-virtual functions

}

The output of the program is:

Derived :: nameof

INVOKED BASE

Derived :: nameof

Invoked by Derived

Note that no matter whether the pointer to the NAMEOF function is to point to the pointer to the base class or pointing to the pointer to the class, it is derived. Because NameOf is a virtual function, and the objects that PBASE and PDERIVED points are derived, so the call function is derived.

Because virtual functions can only be called for class type objects, you can't explain a global or static function as virtual.

You can use the Virtual keyword when you illustrate a heavy load function, but this is not necessary because it is a virtual function.

The virtual function in the base class must be defined unless they are explained as pure (see the "Abstract Class" in the following section on the pure virtual function).

The virtual function call mechanism can be restricted by the scope partition (: :) to clearly define the method of the function name. Consider the previous code, call the base class's PrintBalance with the following code.

CheckingAccount * PChecking = New CheckingAccount (100.00);

PCHECKING-> Account :: printbalance (); // clearly

Account * paccount = pchecking; // Call Account :: PrintBalance

Paccount-> Account :: PrintBalance (); // Explicit

The two modes in the above example limit the call mechanism of the virtual function is limited by the call of PRINTBALANCE.

Abstract class

Abstract classes are like a one-way statement that can be derived from a unique class. You can't create an object for an abstract class, but you can use an abstract class pointer or reference. At least a class containing a pure virtual function is an abstract class. The class from abstract classes must be implemented for pure virtual functions, otherwise they are also abstract classes.

Describe a virtual function as pure, as long as you use a pure explanatory syntax (see the implementation of the "class protocol in the previous chapter"), consider the examples provided in the "virtual function" earlier in this chapter. The intent of class Account is to provide a common function function, and the object of the Account type is too simple and there is not much to use. So Account is a good candidate as an abstract class:

Class Account

{

PUBLIC:

Account (Double D); // Constructor

Virtual double getBalance (); // get balance

Virtual Void PrintBalance () = 0; // Pure Virtual Function

Private:

Double _balance;

}

The only difference in the previous description here is that PrintBalance is illustrative with a pure specifier.

Use abstract class limits

Abstract classes cannot be used as the following:

* Variable or member data

* Parameter type

* Return type of function

* Clear conversion type

Another limitation is that if an abstract class constructor calls a pure virtual function, whether it is direct or indirect, the result is uncertain. However, the destructor of the abstract class constructor can call other member functions.

Abstract pure virtual functions can be defined, but they cannot be called directly with the following syntax:

Abstract Class Name :: Function Name ()

This is useful when the design base class contains a class level of a pure virtual destructive function. Because the destruction function of the base class is usually called in the process of destroying an object, consider the following example: #include

/ / Describe an abstract class with a pure virtual destructor

Class Base

{

PUBLIC:

Base () {}

Virtual ~ base () = 0;

}

/ / Provide a definition of a destructor

Base :: ~ base ()

{

}

Class Derived: Public Base

{

PUBLIC: Derived () {};

~ Derived () {};

}

void main ()

{

Derived * pderived = new derived;

Delete Pderived;

}

When an object referred to by Pderive is destroyed, the destructor of the class Derived is called, and then the destructor in the base class base is called. The empty implementation of the pure virtual function guarantees that the function has at least some operations.

Note: In the previous example, the pure virtual function base :: ~ base is invisible in Derived :: ~ Derived. Of course, it is possible to call the pure virtual function with a fully qualified member function name.

Range rule summary

This section adds some new concepts of the class:

* Elightening

* Global name

* Name and limit name

* Volumen name of the function

* Constructor initializer

Secondary

The use of the name must be unrestricted in its range (until the name of the name). If this name represents a function, then this function must be regarding the number and type of parameters. If there is an unsatisfaction, you have to use member access rules.

Global name

Name of an object, function or enumeration If any function, the class is introduced or prefixed with a global single range operator (: :), and does not use the two-purpose operator as described below.

* Scope disformance (:)

* Objects and referenced members selectors (.)

* Members of the pointer (->)

Name and limit name

The name is "limited name" with the name of the two-purpose scope discerning (: :). The name described later after binocular range identification must be a member of the class or its base class in the left side of the setup.

The name illustrated after the member selector (. Or ->) must be a member of the class type object illustrated on the left side of the setup. The name described in the right side of the member selector can be any class type object, as long as the left side of the set is a class type object, and the class of the object defines a heavy-duty member selector (->), it The object refers to the object refers to a special class type (this provision, discussed in detail in the class member access in Chapter 12 "Overload").

The compiler searched a name in the order below, and discovered later: 1. If the name is used in a function, search in the current block range, otherwise search in the global scope.

2. Search in each enclosed block range, including the outermost function range (this will include a function of the function).

3. If the name is used in a member function, search for this name in the range of this class.

4. Search this name in the base class of this class.

5. Search in the peripheral nested class (if any) or its base class, this search has been searched by the range of the class of the outer package.

6. Search in global scope.

However, you can change the search order as follows:

7. If the name is ::, the forced search is in the global range.

8. If there is a Class, Struct, and UNION keywords in front of the name, the mandatory compiler only searches for Class, Struct, or Union name.

9. The name of the left side of the scope identification can only be the name of Class, Struct, and Union. If a non-static member name is referenced in a static member function, an error message will be generated. Similarly, any non-static group employee in any reference surrounding class will generate an error message because the class is not surrounded by the THIS pointer.

Function parameter name

The parameter name of the function is in the range of the outermost block of the function in the definition of the function. Therefore, they are partial names and after the end of the function, the range disappears.

The parameter name of the function is in the partial scope of the function description (prototype) and disappears in the range after the end of the description.

The default parameter name is in the parameter (they are default), as described in the previous paragraphs, however they cannot access local variables and non-static members. Determination of default parameter values ​​is when the function is called, but their given is in the original range of functions. Therefore, the default parameters of the member function are always in the class range.

Constructor initializer

Constructor Initiator (Initialization Base Class and Members "in Chapter 11," Special Member Functions "are described in the last layer block range of constructive function description. Therefore, they can use the parameter name of the constructor.

转载请注明原文地址:https://www.9cbs.com/read-46723.html

New Post(0)