主頁 > 後端開發 > 深入講解 Laravel 的 IoC 服務容器

深入講解 Laravel 的 IoC 服務容器

2020-09-23 16:10:07 後端開發

 

眾所周知,Laravel 控制反轉 (IoC) / 依賴注入 (DI) 的功能非常強大,遺憾的是, 官方檔案 并沒有詳細講解它的所有功能,所以我決定自己實踐一下,并整理成文,下面的代碼是基于 Laravel 5.4.26 的,其他版本可能會有所不同,

了解依賴注入

我在這里不會詳細講解依賴注入/控制反轉的原則 - 如果你對此還不是很了解,建議閱讀 Fabien Potencier (Symfony 框架的創始人)的 What is Dependency Injection? ,

 

訪問容器

通過 Laravel 訪問 Container 實體的方式有很多種,最簡單的就是呼叫輔助函式 app()

1 $container = app();

 

為了突出重點 Container 類,這里就不贅述其他方式了,

注意: 官方檔案中使用的是 $this->app 而不是 $container

(* 在 Laravel 應用中,Application 實際上是 Container 的一個子類 ( 這也說明了輔助函式 app() 的由來 ),不過在這篇文章中我還是將重點講解 Container 類的方法,)

在 Laravel 之外使用 Illuminate\Container

想要不基于 Laravel 使用 Container,安裝 然后:

use Illuminate\Container\Container;

$container = Container::getInstance();

 

基礎用法

最簡單的用法是通過建構式注入依賴類,

1 class MyClass
2 {
3     private $dependency;
4 
5     public function __construct(AnotherClass $dependency)
6     {
7         $this->dependency = $dependency;
8     }
9 }

 

使用 Container 的 make() 方法實體化 MyClass 類:

$instance = $container->make(MyClass::class);

container 會自動實體化依賴類,所以上面代碼實作的功能就相當于:

$instance = new MyClass(new AnotherClass());

( 假設 AnotherClass 還有需要依賴的類 - 在這種情況下,Container 會遞回式地實體化所有的依賴,)

實戰

 phper在進階的時候總會遇到一些問題和瓶頸,業務代碼寫多了沒有方向感,不知道該從那里入手去提升,對此我整理了一些資料,包括但不限于:分布式架構、高可擴展、高性能、高并發、服務器性能調優、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql優化、shell腳本、Docker、微服務、Nginx等多個知識點高級進階干貨需要的可以免費分享給大家需要的(點擊→)我的官方群677079770

下面是一些基于 PHP-DI 檔案 的例子 - 將發送郵件與用戶注冊的代碼解耦:

 1 class Mailer
 2 {
 3     public function mail($recipient, $content)
 4     {
 5         // 發送郵件
 6         // ...
 7     }
 8 }
 9 class UserManager
10 {
11     private $mailer;
12 
13     public function __construct(Mailer $mailer)
14     {
15         $this->mailer = $mailer;
16     }
17 
18     public function register($email, $password)
19     {
20         // 創建用戶賬號
21         // ...
22 
23         // 給用戶發送問候郵件
24         $this->mailer->mail($email, 'Hello and welcome!');
25     }
26 }
27 use Illuminate\Container\Container;
28 
29 $container = Container::getInstance();
30 
31 $userManager = $container->make(UserManager::class);
32 $userManager->register('[email protected]', 'MySuperSecurePassword!');

 

系結介面與具體實作

通過 Container 類,我們可以輕松實作從介面到具體類到實體的程序,首先定義介面:

interface MyInterface { /* ... */ }
interface AnotherInterface { /* ... */ }

 

宣告實作介面的具體類,具體類還可以依賴其他介面( 或者是像上個例子中的具體類 ):

dependency = $dependency;/n    }/n}","classes":{"has":1}}" data-cke-widget-upcasted="1" data-cke-widget-keep-attr="0" data-widget="codeSnippet">class MyClass implements MyInterface
1 {
2     private $dependency;
3 
4     public function __construct(AnotherInterface $dependency)
5     {
6         $this->dependency = $dependency;
7     }
8 }

 

dependency = $dependency;/n    }/n}","classes":{"has":1}}" data-cke-widget-upcasted="1" data-cke-widget-keep-attr="0" data-widget="codeSnippet">

然后使用 bind() 方法把介面與具體類進行系結:

$container->bind(MyInterface::class, MyClass::class);
$container->bind(AnotherInterface::class, AnotherClass::class);

 

最后,在 make() 方法中,使用介面作為引數:

$instance = $container->make(MyInterface::class);

 

注意: 如果沒有將介面與具體類進行系結操作,就會報錯:

Fatal error: Uncaught ReflectionException: Class MyInterface does not exist

 

這是因為 container 會嘗試實體化介面 ( new MyInterface),這本身在語法上就是錯誤的,

實戰

可更換的快取層:

 1 interface Cache
 2 {
 3     public function get($key);
 4     public function put($key, $value);
 5 }
 6 class RedisCache implements Cache
 7 {
 8     public function get($key) { /* ... */ }
 9     public function put($key, $value) { /* ... */ }
10 }
11 class Worker
12 {
13     private $cache;
14 
15     public function __construct(Cache $cache)
16     {
17         $this->cache = $cache;
18     }
19 
20     public function result()
21     {
22         // 應用快取
23         $result = $this->cache->get('worker');
24 
25         if ($result === null) {
26             $result = do_something_slow();
27 
28             $this->cache->put('worker', $result);
29         }
30 
31         return $result;
32     }
33 }
34 use Illuminate\Container\Container;
35 
36 $container = Container::getInstance();
37 $container->bind(Cache::class, RedisCache::class);
38 
39 $result = $container->make(Worker::class)->result();

 

系結抽象類與具體類

也可以與抽象類進行系結:

$container->bind(MyAbstract::class, MyConcreteClass::class);

 

或者將具體類與其子類進行系結:

$container->bind(MySQLDatabase::class, CustomMySQLDatabase::class);

 

自定義系結

在使用 bind() 方法進行系結操作時,如果某個類需要額外的配置,還通過閉包函式來實作:

$container->bind(Database::class, function (Container $container) {
    return new MySQLDatabase(MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASS);
});

 

每次帶著配置資訊創建一個 MySQLDatabase 類的實體的時候( 下面后講到如何通過 Singletons 創建一個可以共享的實體),都要用到 Database 介面,我們看到閉包函式接收了 Container 的實體作為引數,如果需要的話,還可以用它來實體化其他類:

1 $container->bind(Logger::class, function (Container $container) {
2     $filesystem = $container->make(Filesystem::class);
3 
4     return new FileLogger($filesystem, 'logs/error.log');
5 });

 

還可以通過閉包函式自定義要如何實體化某個類:

1 $container->bind(GitHub\Client::class, function (Container $container) {
2     $client = new GitHub\Client;
3     $client->setEnterpriseUrl(GITHUB_HOST);
4     return $client;
5 });

 

決議回呼函式

可以使用 resolving() 方法來注冊一個回呼函式,當系結被決議的時候,就呼叫這個回呼函式:

1 $container->resolving(GitHub\Client::class, function ($client, Container $container) {
2     $client->setEnterpriseUrl(GITHUB_HOST);
3 });

 

所有的注冊的回呼函式都會被呼叫,這種方法也適用于介面和抽象類:

 1 $container->resolving(Logger::class, function (Logger $logger) {
 2     $logger->setLevel('debug');
 3 });
 4 
 5 $container->resolving(FileLogger::class, function (FileLogger $logger) {
 6     $logger->setFilename('logs/debug.log');
 7 });
 8 
 9 $container->bind(Logger::class, FileLogger::class);
10 
11 $logger = $container->make(Logger::class);

 

還可以注冊一個任何類被決議時都會被呼叫的回呼函式 - 但是我想這可能僅適用于登錄和除錯:

1 $container->resolving(function ($object, Container $container) {
2     // ...
3 });

 

擴展類

你還可以使用 extend() 方法把一個類與另一個類的實體進行系結:

1 $container->extend(APIClient::class, function ($client, Container $container) {
2     return new APIClientDecorator($client);
3 });

 

這里回傳的另外一個類應該也實作了同樣的介面,否則會報錯,

單例系結

只要使用 bind() 方法進行系結,每次用的時候,就會創建一個新的實體( 閉包函式就會被呼叫一次),為了共用一個實體,可以使用 singleton() 方法來代替 bind() 方法:

$container->singleton(Cache::class, RedisCache::class);

 

或者是閉包:

1 $container->singleton(Database::class, function (Container $container) {
2     return new MySQLDatabase('localhost', 'testdb', 'user', 'pass');
3 });

 

為一個具體類創建單例,就只傳這個類作為唯一的引數:

$container->singleton(MySQLDatabase::class);

 

在以上的每種情況下,單例物件都是一次創建,反復使用,如果想要復用的實體已經生成了,則可以使用 instance() 方法,例如,Laravel 就是用這種方式來確保Container 的實體有且僅有一個的:

$container->instance(Container::class, $container);

 

自定義系結的名稱

其實,你可以使用任意字串作為系結的名稱,而不一定非要用類名或者介面名 - 但是這樣做的弊端就是不能使用類名實體化了,而只能使用 make() 方法:

$container->bind('database', MySQLDatabase::class);

$db = $container->make('database');

 

為了同時支持類和介面,并且簡化類名的寫法,可以使用 alias() 方法:

1 $container->singleton(Cache::class, RedisCache::class);
2 $container->alias(Cache::class, 'cache');
3 
4 $cache1 = $container->make(Cache::class);
5 $cache2 = $container->make('cache');
6 
7 assert($cache1 === $cache2);

 

存盤值

你也可以使用 container 來存盤任何值 - 比如:配置資料:

$container->instance('database.name', 'testdb');

$db_name = $container->make('database.name');

 

支持以陣列的形式存盤:

$container['database.name'] = 'testdb';

$db_name = $container['database.name'];

 

在通過閉包進行系結的時候,這種存盤方式就顯示出其好用之處了:

$container->singleton('database', function (Container $container) {
    return new MySQLDatabase(
        $container['database.host'],
        $container['database.name'],
        $container['database.user'],
        $container['database.pass']
    );
});

 

( Laravel 框架沒有用 container 來存盤組態檔,而是用了單獨的 Config 類 - 但是 PHP-DI 用了)

小貼士: 在實體化物件的時候,還可以用陣列的形式來代替 make() 方法:

$db = $container['database'];

 

通過方法 / 函式做依賴注入

到目前為止,我們已經看了很多通過建構式進行依賴注入的例子,其實,Laravel 還支持對任何方法做依賴注入:

function do_something(Cache $cache) { /* ... */ }

$result = $container->call('do_something');

 

除了依賴類,還可以傳其他引數:

1 function show_product(Cache $cache, $id, $tab = 'details') { /* ... */ }
2 
3 // show_product($cache, 1)
4 $container->call('show_product', [1]);
5 $container->call('show_product', ['id' => 1]);
6 
7 // show_product($cache, 1, 'spec')
8 $container->call('show_product', [1, 'spec']);
9 $container->call('show_product', ['id' => 1, 'tab' => 'spec']);

 

可用于任何可呼叫的方法:

閉包

1 $closure = function (Cache $cache) { /* ... */ };
2 
3 $container->call($closure);

 

靜態方法

1 class SomeClass
2 {
3     public static function staticMethod(Cache $cache) { /* ... */ }
4 }
5 $container->call(['SomeClass', 'staticMethod']);
6 // 或者:
7 $container->call('SomeClass::staticMethod');

 

普通方法

1 class PostController
2 {
3     public function index(Cache $cache) { /* ... */ }
4     public function show(Cache $cache, $id) { /* ... */ }
5 }
6 $controller = $container->make(PostController::class);
7 
8 $container->call([$controller, 'index']);
9 $container->call([$controller, 'show'], ['id' => 1]);

 

呼叫實體方法的快捷方式

通過這種語法結構 ClassName@methodName,就 可以達到實體化一個類并呼叫其方法的目:

1 $container->call('PostController@index');
2 $container->call('PostController@show', ['id' => 4]);

 

容器用于實體化類,這意味著:

  1. 依賴項被注入建構式(以及方法),
  2. 如果希望重用這個類,則可以將該類定義為單例類,
  3. 你可以使用介面或任意名稱,而不是具體的類,

例如,這將會啟作用:

1 class PostController
2 {
3     public function __construct(Request $request) { /* ... */ }
4     public function index(Cache $cache) { /* ... */ }
5 }
6 $container->singleton('post', PostController::class);
7 $container->call('post@index');

 

最后,你可以將「默認方法」作為第三個引數,如果第一個引數是一個沒有指定方法的類名,則將呼叫默認的方法, Laravel 使用 事件處理 來實作:

1 $container->call(MyEventHandler::class, $parameters, 'handle');
2 
3 // Equivalent to:
4 $container->call('MyEventHandler@handle', $parameters);

 

方法呼叫系結

可以使用 bindMethod() 方法重寫方法呼叫,例如傳遞其他引數:

1 $container->bindMethod('PostController@index', function ($controller, $container) {
2     $posts = get_posts(...);
3 
4     return $controller->index($posts);
5 });

 

所有這些都會奏效,呼叫閉包而不是的原始方法:

1 $container->call('PostController@index');
2 $container->call('PostController', [], 'index');
3 $container->call([new PostController, 'index']);

 

但是, call() 的任何附加引數都不會傳遞到閉包中,因此不能使用它們,

1 $container->call('PostController@index', ['Not used :-(']);

 

注意: 這個方法不屬于 容器介面, 只是具體的 容器類. 參考 提交的 PR 了解為什么忽略引數,

背景關系系結

有時候,你希望在不同的地方使用介面的不同實作,下面是來自 Laravel 檔案 中的一個例子:

1 $container
2     ->when(PhotoController::class)
3     ->needs(Filesystem::class)
4     ->give(LocalFilesystem::class);
5 
6 $container
7     ->when(VideoController::class)
8     ->needs(Filesystem::class)
9     ->give(S3Filesystem::class);

 

現在, PhotoController 和 VideoController 都可以依賴于檔案系統介面,但是每個都將接收不同的實作,你還可以為 give() 使用閉包,就像使用 bind() 一樣:

1 $container
2     ->when(VideoController::class)
3     ->needs(Filesystem::class)
4     ->give(function () {
5         return Storage::disk('s3');
6     });

 

或者命名依賴項:

1 $container->instance('s3', $s3Filesystem);
2 
3 $container
4     ->when(VideoController::class)
5     ->needs(Filesystem::class)
6     ->give('s3');

 

將引數系結基本型別

你還可以通過將變數名稱傳遞給 needs()(而不是介面)并將值傳遞給 give() 來系結基本型別(字串,整數等):

1 $container
2     ->when(MySQLDatabase::class)
3     ->needs('$username')
4     ->give(DB_USER);

 

您可以使用閉包來延遲檢索值,直到需要它:

1 $container
2     ->when(MySQLDatabase::class)
3     ->needs('$username')
4     ->give(function () {
5         return config('database.user');
6     });

 

在這里你不能傳遞一個類或一個命名的依賴項(例如 give('database.user'))因為它將作為文字值回傳 - 為此你必須使用一個閉包:

1 $container
2     ->when(MySQLDatabase::class)
3     ->needs('$username')
4     ->give(function (Container $container) {
5         return $container['database.user'];
6     });

 

標記

你可以使用容器 tag 來系結相關標記:

$container->tag(MyPlugin::class, 'plugin');
$container->tag(AnotherPlugin::class, 'plugin');

 

然后將所有標記的實體檢索為陣列:

1 foreach ($container->tagged('plugin') as $plugin) {
2     $plugin->init();
3 }

 

tag() 的引數都接受陣列:

1 $container->tag([MyPlugin::class, AnotherPlugin::class], 'plugin');
2 $container->tag(MyPlugin::class, ['plugin', 'plugin.admin']);

 

重新系結

*Note: 這是一個更高級的,只是很少需要-請隨意跳過它! *

在系結或實體已經被使用后需要更改時,可以呼叫 rebinding() 回呼 - 例如,此 Session 類在被 Auth 類使用后被替換,因此需要通知 Auth 類變化:

 1 $container->singleton(Auth::class, function (Container $container) {
 2     $auth = new Auth;
 3     $auth->setSession($container->make(Session::class));
 4 
 5     $container->rebinding(Session::class, function ($container, $session) use ($auth) {
 6         $auth->setSession($session);
 7     });
 8 
 9     return $auth;
10 });
11 
12 $container->instance(Session::class, new Session(['username' => 'dave']));
13 $auth = $container->make(Auth::class);
14 echo $auth->username(); // dave
15 $container->instance(Session::class, new Session(['username' => 'danny']));
16 
17 echo $auth->username(); // danny

 

(有關重新系結的更多資訊, 看 這里 和 這里.)

refresh()

還有一個快捷方法 refresh() 來處理這個常見模式:

1 $container->singleton(Auth::class, function (Container $container) {
2     $auth = new Auth;
3     $auth->setSession($container->make(Session::class));
4 
5     $container->refresh(Session::class, $auth, 'setSession');
6 
7     return $auth;
8 });

 

它還回傳現有實體或系結(如果有的話),因此您可以這樣做:

1 // This only works if you call singleton() or bind() on the class
2 $container->singleton(Session::class);
3 
4 $container->singleton(Auth::class, function (Container $container) {
5     $auth = new Auth;
6     $auth->setSession($container->refresh(Session::class, $auth, 'setSession'));
7     return $auth;
8 });

 

(就個人而言,我發現這種語法更加混亂,并且更喜歡上面更詳細的版本!)

Note: 這些方法不屬于 Container interface, 只有具體 Container class.

覆寫建構式引數

makeWith() 方法允許你將其他引數傳遞給建構式, 它忽略任何現有的實體或單例,并且在創建具有不同引數的類的多個實體時仍然有用,同時仍然注入依賴項:

1 class Post
2 {
3     public function __construct(Database $db, int $id) { /* ... */ }
4 }
5 $post1 = $container->makeWith(Post::class, ['id' => 1]);
6 $post2 = $container->makeWith(Post::class, ['id' => 2]);

 

Note: 在Laravel 5.3及以下版本中,它很簡單 make($class, $parameters). 它是在 Laravel 5.4 被移除, 但后來 重新添加為 makeWith() 在 5.4.16. 在Laravel 5.5中,它似乎將恢復為Laravel 5.3語法.

其他方法

這涵蓋了我認為有用的所有方法 - 但只是為了解決問題,這里是剩下的公共方法的摘要......

bound()

如果類或名稱已與 bind(), singleton(), instance() or alias() 系結,則 bound() 回傳true,

1 if (! $container->bound('database.user')) {
2     // ...
3 }
4 
5 還可以使用陣列訪問語法和 isset():
6 
7 if (! isset($container['database.user'])) {
8     // ...
9 }

 

它可以用 unset() 重置,它洗掉指定的系結/實體/別名,

unset($container['database.user']);
var_dump($container->bound('database.user')); // false

 

bindIf()

bindIf()bind() 做同樣的事情,除了它只注冊一個系結(如果還沒有)(參考上面的 bound()), 它可能用于在包中注冊默認系結,同時允許用戶覆寫它,

$container->bindIf(Loader::class, FallbackLoader::class);

 

沒有 singletonIf() 方法,但你可以使用 bindIf($abstract, $concrete, true) 代替:

$container->bindIf(Loader::class, FallbackLoader::class, true);

 

或者這樣寫全也可以:

if (! $container->bound(Loader::class)) {
    $container->singleton(Loader::class, FallbackLoader::class);
}

 

resolved()

如果已經決議了類 resolved() 則回傳true,

var_dump($container->resolved(Database::class)); // false
$container->make(Database::class);
var_dump($container->resolved(Database::class)); // true

 

我不確定它有什么用處,如果使用 unset() 它會被重置 (可以看上面的 bound()),

unset($container[Database::class]);
var_dump($container->resolved(Database::class)); // false

 

factory()

factory() 方法回傳一個不帶引數的閉包,并呼叫 make()

$dbFactory = $container->factory(Database::class);

$db = $dbFactory();

 

我不確定它有什么用處...

wrap()

wrap() 方法包裝一個閉包,以便在執行時注入它的依賴項, wrap 方法接受一組引數, 回傳的閉包沒有引數:

1 $cacheGetter = function (Cache $cache, $key) {
2     return $cache->get($key);
3 };
4 
5 $usernameGetter = $container->wrap($cacheGetter, ['username']);
6 
7 $username = $usernameGetter();

 

我不確定它有什么用處,因為閉包沒有引數...

Note: 這種方法不屬于 Container interface, 只屬于 Container class.

afterResolving()

afterResolving() 方法與 resolving() 完全相同,只是在「決議」回呼之后呼叫 「決議后」 回呼, 我不確定什么時候會有用...

最后...

  • isShared() - 確定給定型別是否為共享單例/實體
  • isAlias() - 確定給定字串是否是已注冊的別名
  • hasMethodBinding() - 確定容器是否具有給定的方法系結
  • getBindings() - 檢索所有已注冊系結的原始陣列
  • getAlias($abstract) - 決議基礎類/系結名稱的別名
  • forgetInstance($abstract) - 清除單個實體物件
  • forgetInstances() - 清除所有實體物件
  • flush() - 清除所有系結和實體,有效地重置容器
  • setInstance() - 替換 getInstance() 使用的實體(Tip:使用 setInstance(null) 清除它,所以下次它將生成一個新實體)

Note: 最后一節中沒有一個方法是其中的一部分 Container interface.

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/112841.html

標籤:PHP

上一篇:ThinkPHP6 核心分析:系統服務

下一篇:分享學習 PHP 原始碼的方法

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more