程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++多態的完成及道理具體解析

C++多態的完成及道理具體解析

編輯:關於C++

C++多態的完成及道理具體解析。本站提示廣大學習愛好者:(C++多態的完成及道理具體解析)文章只能為提供參考,不一定能成為您想要的結果。以下是C++多態的完成及道理具體解析正文


本文實例講述了C#采取OpenXml給Word文檔添加表格的辦法,長短常適用的操作技能。分享給年夜家供年夜家參考。詳細剖析以下:

這裡將展現若何應用Openxml向Word添加表格. 代碼中表頭和數據我們用的統一個TableRow來添加,其實可以經由過程TableHeader來,其實都一樣。前面我們還會進一步給出若何設置單位格款式。表頭那一行可以本身經由過程設置款式來掌握

示例代碼以下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

namespace AddTableToWord
{
  public class Program
  {
    public static void Main(string[] args)
    {
      List<string[]> lstData = new List<string[]>() { new string[] { "1", "2", "3" }, new string[] { "3", "2", "1" } };
      string[] headerArray = new string[] { "A", "B", "C" };
      AddTable("Test.docx", lstData, headerArray);
    }

    /// <summary>
    /// word外面添加table
    /// </summary>
    /// <param name="wordPath">word文件途徑</param>
    /// <param name="lstData">數據</param>
    /// <param name="headerArray">表頭</param>
    public static void AddTable(string wordPath, List<string[]> lstData, string[] headerArray)
    {
      using (WordprocessingDocument doc = WordprocessingDocument.Open(wordPath, true))
      {
        TableGrid grid = new TableGrid();
        int maxColumnNum = lstData.Select(x => x.Count()).Max();
        for (int index = 0; index < maxColumnNum; index++)
        {
          grid.Append(new TableGrid());
        }

        // 設置表格邊框
        TableProperties tblProp = new TableProperties(
        new TableBorders(
        new TopBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 2 },
        new BottomBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 2 },
        new LeftBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 2 },
        new RightBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 2 },
        new InsideHorizontalBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 2 },
        new InsideVerticalBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 2 }
        )
        );

        Table table = new Table();
        table.Append(tblProp);

        // 添加表頭. 其實有TableHeader對象的,小弟用不來.
        TableRow headerRow = new TableRow();
        foreach (string headerStr in headerArray)
        {
          TableCell cell = new TableCell();
          cell.Append(new Paragraph(new Run(new Text(headerStr))));
          headerRow.Append(cell);
        }
        table.Append(headerRow);

        // 添加數據
        foreach (string[] rowArray in lstData)
        {
          TableRow row = new TableRow();
          foreach (string strCell in rowArray)
          {
            TableCell cell = new TableCell();
            cell.Append(new Paragraph(new Run(new Text(strCell))));
            row.Append(cell);
          }
          table.Append(row);
        }

        doc.MainDocumentPart.Document.Body.Append(new Paragraph(new Run(table)));
      }
    }
  }
}

履行出現成果以下:

願望本文所述對年夜家的C#法式設計有所贊助

��熟蒂落了。

正如許多學員所想,在例1-1的法式中,我們曉得pAn現實指向的是fish類的對象,我們願望輸入的成果是魚的呼吸辦法,即挪用fish類的breathe辦法。這個時刻,就該輪到虛函數退場了。

後面輸入的成果是由於編譯器在編譯的時刻,就曾經肯定了對象挪用的函數的地址,要處理這個成績就要應用遲綁定(late binding)技巧。當編譯器應用遲綁准時,就會在運轉時再去肯定對象的類型和准確的挪用函數。而要讓編譯器采取遲綁定,就要在基類中聲明函數時應用virtual症結字(留意,這是必需的,許多學員就是由於沒有應用虛函數而寫出許多毛病的例子),如許的函數我們稱為虛函數。一旦某個函數在基類中聲明為virtual,那末在一切的派生類中該函數都是virtual,而不須要再顯式地聲明為virtual。
上面修正例1-1的代碼,將animal類中的breathe()函數聲明為virtual,以下:

#include <iostream.h>
class animal
{
public:
 void sleep()
 {
  cout<<"animal sleep"<<endl;
 }
 virtual void breathe()
 {
  cout<<"animal breathe"<<endl;
 }
};

class fish:public animal
{
public:
 void breathe()
 {
  cout<<"fish bubble"<<endl;
 }
};
void main()
{
 fish fh;
 animal *pAn=&fh; // 隱式類型轉換
 pAn->breathe();
}

年夜家可以再次運轉這個法式,你會發明成果是“fish bubble”,也就是依據對象的類型挪用了准確的函數。
那末當我們將breathe()聲明為virtual時,在面前產生了甚麼呢?

編譯器在編譯的時刻,發明animal類中有虛函數,此時編譯器會為每一個包括虛函數的類創立一個虛表(即vtable),該表是一個一維數組,在這個數組中寄存每一個虛函數的地址。關於例1-2的法式,animal和fish類都包括了一個虛函數breathe(),是以編譯器會為這兩個類都樹立一個虛表,(即便子類外面沒有virtual函數,然則其父類外面有,所以子類中也有了)以下圖所示:




 

那末若何定位虛表呢?編譯器別的還為每一個類的對象供給了一個虛表指針(即vptr),這個指針指向了對象所屬類的虛表。在法式運轉時,依據對象的類型去初始化vptr,從而讓vptr准確的指向所屬類的虛表,從而在挪用虛函數時,就可以夠找到准確的函數。關於例1-2的法式,因為pAn現實指向的對象類型是fish,是以vptr指向的fish類的vtable,當挪用pAn->breathe()時,依據虛表中的函數地址找到的就是fish類的breathe()函數。

恰是因為每一個對象挪用的虛函數都是經由過程虛表指針來索引的,也就決議了虛表指針的准確初始化長短常主要的。換句話說,在虛表指針沒有准確初始化之前,我們不克不及夠去挪用虛函數。那末虛表指針在甚麼時刻,或許說在甚麼處所初始化呢?

謎底是在結構函數中停止虛表的創立和虛表指針的初始化。還記得結構函數的挪用次序嗎,在結構子類對象時,要先挪用父類的結構函數,此時編譯器只“看到了”父類,其實不曉得前面能否後還有繼續者,它初始化父類對象的虛表指針,該虛表指針指向父類的虛表。當履行子類的結構函數時,子類對象的虛表指針被初始化,指向本身的虛表。關於例2-2的法式來講,當fish類的fh對象結構終了後,其外部的虛表指針也就被初始化為指向fish類的虛表。在類型轉換後,挪用pAn->breathe(),因為pAn現實指向的是fish類的對象,該對象外部的虛表指針指向的是fish類的虛表,是以終究挪用的是fish類的breathe()函數。

要留意:關於虛函數挪用來講,每個對象外部都有一個虛表指針,該虛表指針被初始化為本類的虛表。所以在法式中,不論你的對象類型若何轉換,但該對象外部的虛表指針是固定的,所以呢,能力完成靜態的對象函數挪用,這就是C++多態性完成的道理。

總結(基類有虛函數):
1. 每個類都有虛表。

2. 虛表可以繼續,假如子類沒有重寫虛函數,那末子類虛表中依然會有該函數的地址,只不外這個地址指向的是基類的虛函數完成。假如基類有3個虛函數,那末基類的虛表中就有三項(虛函數地址),派生類也會有虛表,至多有三項,假如重寫了響應的虛函數,那末虛表中的地址就會轉變,指向本身的虛函數完成。假如派生類有本身的虛函數,那末虛表中就會添加該項。

3. 派生類的虛表中虛函數地址的分列次序和基類的虛表中虛函數地址分列次序雷同。

這就是C++中的多態性。當C++編譯器在編譯的時刻,發明animal類的breathe()函數是虛函數,這個時刻C++就會采取遲綁定(late binding)技巧。也就是編譯時其實不肯定詳細挪用的函數,而是在運轉時,根據對象的類型(在法式中,我們傳遞的fish類對象的地址)來確認挪用的是哪個函數,這類才能就叫做C++的多態性。我們沒有在breathe()函數前加virtual症結字時,C++編譯器在編譯時就肯定了哪一個函數被挪用,這叫做晚期綁定(early binding)。

C++的多態性是經由過程遲綁定技巧來完成的。

C++的多態性用一句話歸納綜合就是:在基類的函數前加上virtual症結字,在派生類中重寫該函數,運轉時將會依據對象的現實類型來挪用響應的函數。假如對象類型是派生類,就挪用派生類的函數;假如對象類型是基類,就挪用基類的函數。

虛函數是在基類中界說的,目標是不肯定它的派生類的詳細行動。例:
界說一個基類:class Animal//植物。它的函數為breathe()//呼吸。
再界說一個類class Fish//魚 。它的函數也為breathe()
再界說一個類class Sheep //羊。它的函數也為breathe()

為了簡化代碼,將Fish,Sheep界說成基類Animal的派生類。
但是Fish與Sheep的breathe紛歧樣,一個是在水中經由過程水來呼吸,一個是直接呼吸空氣。所以基類不克不及肯定該若何界說breathe,所以在基類中只界說了一個virtual breathe,它是一個空的虛函數。具本的函數在子類平分別界說。法式普通運轉時,找到類,假如它有基類,再找它的基類,最初運轉的是基類中的函數,這時候,它在基類中找到的是virtual標識的函數,它就會再回到子類中找同名函數。派生類也哨子類。基類也叫父類。這就是虛函數的發生,和類的多態性(breathe)的表現。

這裡的多態性是指類的多態性。
函數的多態性是指一個函數被界說成多個分歧參數的函數,它們普通被存在頭文件中,當你挪用這個函數,針對分歧的參數,就會挪用分歧的同名函數。例:Rect()//矩形。它的參數可所以兩個坐標點(point,point)也能夠是四個坐標(x1,y1,x2,y2)這叫函數的多態性與函數的重載。

類的多態性,是指用虛函數和延遲綁定來完成的。函數的多態性是函數的重載。

普通情形下(沒有觸及virtual函數),當我們用一個指針/援用挪用一個函數的時刻,被挪用的函數是取決於這個指針/援用的類型。即假如這個指針/援用是基類對象的指針/援用就挪用基類的辦法;假如指針/援用是派生類對象的指針/援用就挪用派生類的辦法,固然假如派生類中沒有此辦法,就會向上到基類外面去尋覓響應的辦法。這些挪用在編譯階段就肯定了。

當設計到多態性的時刻,采取了虛函數和靜態綁定,此時的挪用就不會在編譯時刻肯定而是在運轉時肯定。不在零丁斟酌指針/援用的類型而是看指針/援用的對象的類型來斷定函數的挪用,依據對象中虛指針指向的虛表中的函數的地址來肯定挪用哪一個函數。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved