網站建設網站維護的具體內容是什么seo推廣員是做什么的
?一丶聲明、定義、實例化、初始化
1、首先我們來討論在C/C++中的聲明和定義..
1)我們先從函數聲明和定義說起...
一般我們在C++里都會先定義一個函數,然后再Main函數前將函數聲明,比如:
//函數聲明
int Add(int);int Main
{}
//函數定義
int Add(int)
{
}
聲明就是告訴編譯器,咱這兒有一個名字叫Add,返回值和參數需要int的函數,就給你說一聲
定義就是在內存中拓展出了這么一片空間給該函數分配內存
所以你看,定義只能定義一次吧,如果你稍微變了個啥,這函數就不是自己了,但是聲明你隨意,你想聲明啥聲明啥,我內存沒有啊,也就不存在調用,就相當于你在那空喊,沒人理你...
2)我們再來討論變量的聲明和定義...
在C++里面,我們是這么聲明和定義的
//聲明
extren int var;
typeof int INT;
struct Node;//定義
extern int ble = 10;
所以聲明是不開辟內存空間的,而定義必須占用內存空間...
因此,在C++中,常用到定義和聲明的就是函數和變量。。。
3)實例化和初始化
在面向對象的編程中,通常把用類創(chuàng)建對象的過程成為實例化,注意,他一般用在類上
初始化可以理解為給聲明的對象賦值的過程,也就相當于定義
2:C#中的定義和聲明
????????在C#中,其實和C++大概一樣的,不過在C#中,你定義一個函數以后,可以直接進行使用,而不用在主函數體上方聲明函數,至于變量的定義和聲明,和C++一樣,如果是int a ;只是聲明,沒有定義,使用int? a = 10;這樣才算創(chuàng)建出來了變量。
二、委托和事件
一:委托
????????我說的這一點很重要,所有的代碼語言創(chuàng)造者母語都是英語,我們從英語翻譯到中文的過程中難免會存在一些不太能還原本意的詞,比如我之前一直不理解構造函數和析構函數,只知道這倆貨作用相反,直到我看到了它的英文意思,Construstor/Distructor,我才徹底理解了他們的作用。
? ? ? ? 接下來我們來看委托,Delegate,來看兩個例句,深入理解Delegate...
Can?you?delegate?some?tasks?or?projects?????????????????????????????????你能夠分配一些任務或者項目嗎?
So?why?not delegate?more?work?to your?employees?? ??所以你為啥不給你的員工多分分配點任務?
從上面的句子中我們可以看到,他就是分配,也就是委托的意思(但是感覺可能有些人對委托的理解不如分配來的直接,至少對我來說是這樣)
微軟官方的解釋是委托可以獲取一個或多個方法,但是類型和返回值必須和方法相同,可以理解成委托是方法的抽象,也就是說定義一個方法的模板,至于這個方法具體是怎么樣的,就由方法自己去實現。這點和函數指針很像...后續(xù)寫單播委托時候再添加這部分內容。
話不多說,看代碼...
1:單播委托——一次只能裝進去一個方法
Public delegate 返回值 MyDelegate(參數1,參數2)
就這么一步,我們就把委托定義出來了,接下來要做的就是把這玩意兒實例化出來,那我們怎么把我們的方法給委托呢?
第一點就是我們需要有和定義的委托類型一致的返回值和參數列表
這點比較像C語言函數指針,我們用函數指針來說明上一個問題
//使用Typedef將該函數指針聲明為一種類型,它是指向兩個參數為int,返回值為int的函數指針
typedef int (*Calculator)(int x , int y);int Add(int a ,int b)
{return a+b;
}int Multiply(int a ,int b)
{return a*b;
}//函數指針的使用Calculator Pointer1 = &Add;
Calculator Pointer2 = &Multiply;//這樣我們在調用函數的時候就不再寫函數,而是采用函數指針的方法,間接的指向了該類型的函數
Pointer1(0,1);
Pointer2(1,2);
從上面的函數指針我們可以看出,我們在注冊方法的時候,可以間接的聲明一個和該方法類型和返回值都一致的指針類型,從而調用函數指針即可,那么我們的委托和它是類似的....
接下來看我們的委托的實例化和方法的插入,可以使用new,也可以直接引用方法:
//實例化委托
MyDelegate myDelegate = new MyDelegate(Function);//簡化寫法
myDelegate = Telegate;返回值類型 Function(參數1,參數2)
{方法體;
}
委托的調用,可以使用Invoke,也可以直接寫委托名+()
可以通過Invoke進行調用委托
myDelegate.Invoke();也可以直接myDelegate();
2:多播委托——一次裝多個方法,但不安全
在上面的方法添加環(huán)節(jié),我們只需要做小小的修改
myDelegate += ChangeColor;
myDelegate += Log;
但是其實我們沒事兒也不會這么干,這樣長期下來有可能會存在內存泄漏,如果順序執(zhí)行列表中方法有一個出錯了,后面的就都不會執(zhí)行了,所以我們還有其他更好的選擇
3:Action委托和Func委托
大多數的情況下,我們不太需要自己去聲明委托,而是使用現成的委托即可,C#為我們內置了兩種泛型委托
1)Action委托——返回值必須為空,參數可有可無
//聲明無參數的Action委托
Action action;//聲明有參數的Action委托
Action<string,float> action1;//Action的使用
action = new Action(同參數的方法1)
action1 = new Action<string ,float> (sayhello);//sayhello方法
public void SayHello(string name,float num)
{Debug.log(sting.Fromat("{0} has {1} ChampionShips .",name,num));
}
2) Func委托——返回值必須有,但是參數可有可無
//聲明Func委托,前面是參數,后面是返回值
Func<double,double,double> func1;//使用
func1 = new Func<double,double,double>(Add);//跟Func結構一樣的方法
public double Add(doublea ,double b)
{return a+b;
}
二:事件
事件,使對象或類具備通知能力
日常開發(fā)中,自己聲明事件的機會比較少,一般是用已有事件比較多...
java中沒有委托,事件這么一說,只用Interface來實現
1:五個重要因素
事件的擁有者
事件成員(Event)
事件的響應者(Event Subscriber)
事件處理器(Event handler,本質上是回調方法)
事件訂閱(+=)
他們之間的關系可以如下幾種:
①事件的擁有者類和事件的響應者是不同的類
using System.Timers;
using UnityEngine;public class EventTimothyLiu1 : MonoBehaviour
{private void Start(){//世間間隔,每過1s就觸發(fā)Elesap事件Timer timer = new Timer();timer.Interval = 1000;Boy boy = new();Girl girl = new();timer.Elapsed += boy.OnAction;timer.Elapsed += girl.OnAction;timer.Start();}
}
public class Boy
{internal void OnAction(object sender, ElapsedEventArgs e){Debug.Log("1");}
}
public class Girl
{internal void OnAction(object sender, ElapsedEventArgs e){Debug.Log("2");}
}
在該示例中,事件的擁有者是Timer類,事件的響應者是自定義的Boy,Girl類
所以事件的響應者和擁有者是兩個類
第二個例子:
using System;
using System.Windows.Forms;namespace EventLiu
{class Program{static void Main(string[] args){//事件的擁有者:FormForm form = new Form();//事件的響應者:ControllerController controller = new Controller(form);form.ShowDialog();}}class Controller{private Form form;/// <summary>/// CTOR再加Tab即可編寫出構造器/// 也叫構造函數,每個類都必須有/// 在構建類的引用時自動運行的方法!/// </summary>public Controller(Form form){if (form!= null){//this就是類的實例//this后是我們定義的字段,后面是參數formthis.form = form;//Click是事件this.form.Click += this.FormClicked;}}/// <summary>/// 事件處理器/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void FormClicked(object sender, EventArgs e){this.form.Text = DateTime.Now.ToString();}}
}
上述的代碼段中,我們引入了Form名稱空間
事件的擁有者是Form,事件的響應者是Controller
②事件的擁有者同時也是事件的響應者
using System;
using System.Windows.Forms;namespace EventLiu
{class Program1{static void Main(string[] args){//事件的擁有者:myForm//事件的接受者:myFormMyForm myForm = new MyForm();//事件:ClickmyForm.Click += myForm.FormClicked;}}/// <summary>/// 繼承Form/// </summary>class MyForm : Form{/// <summary>/// 事件的處理器/// </summary>/// <param name="sender"></param>/// <param name="e"></param>internal void FormClicked(object sender, EventArgs e){this.Text = DateTime.Now.ToString();}}
}
事件的擁有者是Form
事件的響應者也是myForm的實例
③事件的擁有者是事件的響應者的成員(頻率最高)
事件的響應者用自己的方法訂閱者自己的字段成員的事件
using System;
using System.Windows.Forms;namespace EventLiu
{class Program1{static void Main(string[] args){MyForm myForm = new MyForm();myForm.ShowDIalog();}}class MyForm :Form{private TextBox textBox;//從訂閱看事件擁有者就是buttonprivate Button button;public MyForm(){this.textBox = new TextBox();this.button = new Button();this.Controls.Add(this.button);this.Controls.Add(this.textBox);//Click是事件//事件的響應者是this,也就是MyForm的實例對象this.button.Click += this.ButtonClicked;}//事件處理器private void ButtonClicked(object sender, EventArgs e){this.textBox.Text = "Hello";}}
}
在該段代碼中,我們自己創(chuàng)建MyForm類,繼承自Form
事件的擁有者是該類中的成員Button,事件的響應者是該類的實例化
也就是兒子有事件,爸爸訂閱了。? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
④事件的響應者是事件的擁有者的成員
2:事件的完整聲明格式
3:事件的簡單聲明格式
3:UnityEvent
4:UnityAction
5:模板方法,回調函數
三、CPU和GPU的區(qū)別
CPU和GPU從名字上來說,一個是Central Processing Unit,一個是Graphic Processing Unit
,后者是專門對圖像進行處理的,為什么要針對圖像處理要用專門的處理單元呢?
GPU(圖形處理器)是圖形系統(tǒng)結構的重要元件,是連接計算機和顯示終端的紐帶
就是說如果你要把圖像顯示在顯示器上,必須要使用GPU,而GPU是如何工作的?可以參見下面的視頻:
GPU工作原理_嗶哩嗶哩_bilibili
來看看CPU和GPU的內部結構
?從圖中可以看出,我們GPU有大量的運算單元,而CPU只有簡單的4個ALU,而我GPU有成百上千的計算單元,我們單論單線的計算能力,比如你把一個數學中的復雜運算交給CPU,效率會變得特別高,它也理所當然能夠完成任務,但是在圖形計算方面,我們并沒有呢么多高級運算,而是要進行大量的矩陣運算變換,也就是所謂的頂點計算→光柵化計算→紋理帖圖→像素處理→最終輸出
而在這樣的運算中,都是一個一個像素點進行處理的,對于高像素的屏幕,有非常多的像素點,而對于每個像素點都得進行大量的計算,所以使用CPU效率實在太低,此時如果使用GPU,效率會變得很高,但是要注意的是:
光影都是CPU計算的,GPU只有2個工作,1多邊形生成。2為多邊形上顏色。
如下視頻可以簡單的告訴你CPU和GPU的區(qū)別...
NVIDIA現場形象展示CPU和GPU工作原理上的區(qū)別_嗶哩嗶哩_bilibili
這就是簡單的CPU和GPU的區(qū)別介紹
四、堆和棧的區(qū)別
計算機的邏輯運算在CPU中進行,CPU中又有ALU(Arithmetic/Logic Unit)和CU(Control Unit)
ALU是由"And Gate"(與門) 和"Or Gate"(或門)構成的算術邏輯單元,主要功能是進行二位元的算術運算
而CU是負責程序的流程管理。正如工廠的物流分配部門,控制單元是整個CPU的指揮控制中心,由指令寄存器IR(Instruction Register)、指令譯碼器ID(Instruction Decoder)和操作控制器OC(Operation Controller)三個部件組成
問題就出在,在計算機進行計算時,并不是將所有的數據都放在CPU,而是放在內存中,CPU對其進行調用,將內存中的數據放在寄存器中,所以CPU根據計算需求對內存進行調用,而我們說的堆和棧就是在內存中的兩個區(qū)域
棧內存(Stack):棧中存放的是對象的引用及對象方法中的局部變量的值(參數的值),由操作系統(tǒng)自動分配釋放
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?棧內存是動態(tài)分配和靜態(tài)分配結合,是局部的
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 入棧和出棧遵循彈夾原則,先進后出,后進先出
????????????????????????????????棧的生長方向向下,內存地址由高到低
堆內存(Heap):堆中存放的是實例對象及成員變量的值(屬性的值),堆由開發(fā)人員分配和釋放, 若開發(fā)人員不釋放,程序結束時由 OS 回收
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 堆是動態(tài)分配的,是全局的
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 堆可以被看成是一棵樹,如:堆排序
????????????????????????????????堆的生長方向向上,內存地址由低到高
五、值類型和引用類型/裝箱和拆箱
值類型:C#的所有值類型均隱式派生自System.ValueType。值類型是直接存儲該值的
? ? ? ? ? ? ? byte,short,int,long,float,double,decimal,char,bool 和?struct?統(tǒng)稱為值類型。
? ? ? ? ? ? ? 值類型在聲明后,不管有沒有賦值,都已分配了內存
引用類型:引用類型是存儲該類型的引用
? ? ? ? ? ? ? string 和 class,數組,接口,委托統(tǒng)稱為引用類型。
? ? ? ? ? ? ? ?引用類型在聲明后,只在棧中分配一小片內存用于容納一個地址,而此時并沒有為其分配堆上的內存空間;當使用 new 創(chuàng)建一個類的實例時,分配堆上的空間。
裝箱和拆箱:
知道了值類型和引用類型以后,就牽扯一件事,這兩種類型之間需要轉換,比如
ArrayList arraylist = new ArrayList();
arraylist[0].add(1);
arraylist[1].add('a');
...
此時我們需要將值類型的1和字符類型的a放在引用類型的數組中arraylist中,我們知道所有數據類型的基類是object,所以要想把值類型的對象放在引用類型的對象中,需要將值類型轉換為object,這種轉化是隱士進行的,此時上面的數組中的0號元素就是引用類型的
再看下一段
int a = (int)arraylist[0];
此時我們是把上面數組中的引用類型轉化為值類型int,此時就進行了強轉化
六、Array/ArrayList/List
明了...
七、抽象類/接口以及區(qū)別
具體類→抽象類→接口:越來越抽象,內部實現的東西越來越少
?
1:抽象類
抽象類是未完全實現邏輯的類
抽象類為復用而生,專門用作基類
封裝確定的,開放不確定的,推遲到合適的子類來實現
抽象類的成員可以是私有的,受保護的,內部的
using UnityEngine;
using System;public class OCPandObstract : MonoBehaviour
{private void Start(){Vehicle1 vehicle1 = new();vehicle1.Run();//無法實例化抽象類,只能實現其子類Vehicle2 v = new Car2();v.Stop();}
}
/// <summary>
/// 一個汽車類,一個卡車類,里面的方法都是一樣的,重新寫兩個類就很繁瑣
/// </summary>
class Car
{public void Run(){Console.WriteLine("Car is running ..");}public void Stop(){Console.WriteLine("Stopped...");}
}
class Truck
{public void Run(){Console.WriteLine("Car is running ..");}public void Stop(){Console.WriteLine("Stopped...");}
}/// <summary>
/// 簡化方法1:使用虛函數進行重寫,符合開閉原則
/// </summary>
class Vehicle1
{public void Stop(){Console.WriteLine("Stopped...");}public virtual void Run(){Console.WriteLine("Vehicle is running...");}
}
class Car1:Vehicle1
{public override void Run(){Console.WriteLine("Car1 is running...");}
}
class Truck1:Vehicle1
{public override void Run(){Console.WriteLine("Truck1 is running...");}
}/// <summary>
/// 簡化方法2:,使用抽象類,抽象函數,也就是純虛方法
/// 此時抽象類無法實例化
/// </summary>
abstract class Vehicle2
{public void Stop(){Console.WriteLine("Stopped...");}/// <summary>/// Run需要重寫,因此作為抽象函數,沒有實現部分/// </summary>public abstract void Run();
}
class Car2 : Vehicle2
{public override void Run(){Console.WriteLine("Car2 is running...");}
}
class Truck2 : Vehicle2
{public override void Run(){Console.WriteLine("Truck2 is running...");}
}
上述代碼一步步介紹了為什么使用抽象類,怎么使用抽象函數
2:接口
接口是完全未實現邏輯的“類”
接口是“純虛類”,只有成員函數,成員全部public
接口為解耦而生,高內聚,低耦合
繼承自接口的類必須實現接口中的函數,且自身無法實現
接口中可以有屬性,方法,事件,索引
接口成員默認是公開的,且不加任何修飾符
using UnityEngine;public class OCPandInterface : MonoBehaviour
{}
/// <summary>
/// 純抽象類
/// </summary>
abstract class AnimalBase
{abstract public void Eat();abstract public void Sleep();abstract public void Walk();
}
/// <summary>
/// 抽象類,繼承自純抽象類
/// </summary>
abstract class Animal : AnimalBase
{public override void Eat(){}public override void Sleep(){}
}
/// <summary>
/// 具體類
/// </summary>
class Dog:Animal
{public override void Walk(){}
}
/// <summary>
/// 具體類
/// </summary>
class Duke : Animal
{public override void Walk(){}
}/// <summary>
/// 引入interface,它成員變量默認為抽象且公開
/// </summary>
interface IAnimalBase
{void Eat();void Sleep();void Walk();
}
abstract class Animal1 : IAnimalBase
{/// <summary>/// 繼承接口以后,也變成了具體方法/// </summary>public void Eat(){}public void Sleep(){}public abstract void Walk();
}
class Dog1 : Animal1
{public override void Walk(){}
}
class Duke1 : Animal1
{public override void Walk(){}
}
上述代碼描述了如何引入的接口,且接口如何定義
3:抽象類和接口的區(qū)別
八、屬性(Get訪問器和Set)和字段,static關鍵字
首先看看C#有哪些成員
?1:字段(field)
字段是一種表示與對象或類型關聯(lián)的變量,也叫成員變量
看如下代碼,可以看出字段是如何一步步變成屬性
using UnityEngine;
using System;public class GetSetProperty : MonoBehaviour
{private void Start(){//一、未使用Get/Set屬性Student stu1 = new();Student stu2 = new();Student stu3 = new();stu1.Age = 20;stu2.Age = 20;stu3.Age = 20;//此時如果有一個年齡被不小心修改了,比如第三個學生年齡修改成200,這種字段被污染很難發(fā)現int avgAge = (stu1.Age + stu2.Age + stu3.Age) / 3;//Debug.Log(avgAge);//二、使用Get/Set方法Student1 stu4 = new();Student1 stu5 = new();Student1 stu6 = new();//此時如果不小心將年齡修改成200,程序就會報錯//可以使用try catch,程序就不會崩潰stu4.SetAge(20);stu5.SetAge(20);//stu6.SetAge(200);int avgAge1 = (stu4.GetAge() + stu5.GetAge() + stu6.GetAge())/3;try{stu6.SetAge(200);Debug.Log(avgAge1);}catch (Exception ex){Debug.Log(ex.Message);}//三、使用Get/Set訪問器Student2 stu7 = new Student2();Student2 stu8 = new Student2();Student2 stu9 = new Student2();stu7.Age = 20;stu8.Age = 20;stu8.Age = 20;int avgAge2 = (stu7.Age + stu8.Age + stu9.Age) / 3;Debug.Log(avgAge2);}
}
class Student
{public int Age;public int Score;
}/// <summary>
/// 二、此時使用Get,Set函數,可以對字段進行約束
/// </summary>
class Student1
{private int age;public int GetAge(){return this.age;}/// <summary>/// /// </summary>/// <param name="value"></param>public void SetAge(int value){if (value >= 0 && value <= 120){this.age = value;}elsethrow new System.Exception("value has error");}
}/// <summary>/// 三、使用微軟的Get/Set訪問器/// </summary>
class Student2
{private int age;public int Age{get{return age;}set{if (value >= 0 && value <= 120){this.age = value;}else{throw new Exception("Age value has error");}}}
}
剛開始我們使用字段存儲數據
但是考慮到暴露的緣故,我們引入了Get和Set函數來對字段進行約束
微軟對Get/Set訪問器進行了簡化,就形成了Get/Set訪問器
可以使用PropFull進行快速生成屬性
2:屬性(Prooerty)
屬性是一種用于訪問對象或類型的特征的成員,屬性是字段的自然擴展
3:屬性和字段的關系
屬性和字段都是用于表示實體的狀態(tài)
屬性大多數情況是字段的包裝器
建議:永遠使用屬性來暴露數據,即字段永遠是private或protected的
4:static關鍵字
static關鍵字可以在類的定義,變量的聲明,方法的定義中
變量前加上Static,變量成為靜態(tài)變量,靜態(tài)變量的內存分布在全局靜態(tài)區(qū),相當于它就是全局變量,Time.timescale就是靜態(tài)變量
在函數前加上static,函數就變成了靜態(tài)函數,直接類名+函數名就可以調用,比如Debug,或者Find函數都是靜態(tài)函數
在類名的前面加上static,該類就變成了靜態(tài)類,靜態(tài)類不需要被實例化,它不能繼承,靜態(tài)類內的成員都需要帶上static關鍵字,但是靜態(tài)函數不需要一定在靜態(tài)類中
一般情況下,將工具函數或者工具類,做成靜態(tài)類或者靜態(tài)函數。
九、匿名方法/λ表達式的用法/LinQ用法
十、字典,隊列,哈希表,Stack和Queue
十一、Unity中AB包何時使用以及如何使用
十二、Unity中的數據加載方式有幾種?
13:容器/迭代器
14:射線檢測,碰撞檢測
15:圖形學的渲染流程是什么?
16:有哪些常見的渲染算法