No video

Virtual Destructors in C++

  Рет қаралды 104,183

The Cherno

The Cherno

6 жыл бұрын

Patreon ► / thecherno
Twitter ► / thecherno
Instagram ► / thecherno
Discord ► thecherno.com/discord
Series Playlist ► thecherno.com/cpp
Thank you to the following Patreon supporters:
- Dominic Pace
- Kevin Gregory Agwaze
- Sébastien Bervoets
- Tobias Humig
- Peter Siegmund
- Kerem Demirer
Gear I use:
-----------------
BEST laptop for programming! ► geni.us/pakTES
My FAVOURITE keyboard for programming! ► geni.us/zNhB
FAVOURITE monitors for programming! ► geni.us/Ig6KBq
MAIN Camera ► geni.us/t6xyDRO
MAIN Lens ► geni.us/xGoDWT
Second Camera ► geni.us/CYUQ
Microphone ► geni.us/wqO6g7K

Пікірлер: 140
@johnpawlicki1184
@johnpawlicki1184 6 жыл бұрын
Where were you in the 90s when I was teaching myself C++? Back them this was very difficult to figure out. XLNT video.
@cajogos
@cajogos 4 жыл бұрын
He was learning to talk
@chiyungchu9463
@chiyungchu9463 3 жыл бұрын
He hasnt born yet.
@pastori2672
@pastori2672 Ай бұрын
hi unc
@SuperKhezar
@SuperKhezar 6 жыл бұрын
My final is due tomorrow. This video has been a great help. Infact the whole series on C++ is marvellous!
@MudHoleCreation
@MudHoleCreation 6 жыл бұрын
Been programming for years never knew this. Thx man.
@drwisdom1
@drwisdom1 5 жыл бұрын
I cannot emphasize how important a subject this is. One of the harder things to debug in C++ is when you think a destructor is being called, but is not because it was not virtual. This is a serious programming error that must be avoided by following this rule: always make destructors virtual with one exception. If the class is a base class (not derived), you don't ever expect to derive from it, and you put the comment "// non-virtual object!" right above the class definition, then the destructor can be non-virtual. If you ever want to derive from it you will see the comment indicating you need to add virtual to the base destructor.
@inara9888
@inara9888 5 жыл бұрын
Duuddeeeee GOD BLESS YOU! It was SOOO easy to understand OMG!
@rcookie5128
@rcookie5128 6 жыл бұрын
damn, so much potential for memory leaks..
@huunoidoan
@huunoidoan 5 жыл бұрын
We can use sharedptr pr uniquedptr to avoid memory leak.
@aldeywahyuputra5719
@aldeywahyuputra5719 4 жыл бұрын
"With great power comes great responsibility" -popularized in Spider-Man Well, here you are provided with virtually limitless possibility to do whatever you want and with freedom you have -- in return you need to handle it all by yourself.
@c0ldw1nd27
@c0ldw1nd27 3 жыл бұрын
@@huunoidoan Well, if you have 2 shared pointers and a circular dependency between both you can also have a memory leak.
@fleckgames5261
@fleckgames5261 6 жыл бұрын
Nice video! Keep up the good work!
@Cc618.
@Cc618. 6 жыл бұрын
Thank you so much for your amazing videos!
@FuryOnStage
@FuryOnStage 6 жыл бұрын
How come I've never come across this before? Thank you so much. This was great.
@josiahlamb
@josiahlamb 3 жыл бұрын
Can't believe you actually have a video on this topic! Thought I was headed for some head-scratching on cppreference (which ain't even a bad site).
@pratiktata3388
@pratiktata3388 6 жыл бұрын
hey man, you are the reason why I started coding you make it look simple and interesting. your videos are like suspense movies, I just wanna watch more and more.
@dadlord689
@dadlord689 6 жыл бұрын
This is just what I would like to see next. Magic.
@piotrs7632
@piotrs7632 6 жыл бұрын
Can you make video about (virtual inheritance, diamond problem, multiple inheritance) ? How to use it and how it works? You are making good job and talking about difficult things very clearly. Greetings to You.
@huyvole9724
@huyvole9724 6 жыл бұрын
I used to see Virtual inheritance solve multi inheritance
@dmitrimcguckin299
@dmitrimcguckin299 6 жыл бұрын
Piotr S Multiple inheritance isn't usually a good idea, just FYI.
@piotrs7632
@piotrs7632 6 жыл бұрын
yes, i heared about it, but i just want to now how it works in background, because it is an element of language and good to know that.
@omkarpatil2094
@omkarpatil2094 3 жыл бұрын
this was REALLY GOOD. thank you.
@nikoszervo
@nikoszervo 6 жыл бұрын
Nice video and extremely useful to know!!!
@a740g
@a740g Жыл бұрын
2022. This stuff never gets old. Thanks Cherno!
@Xxp0r
@Xxp0r 6 жыл бұрын
Exception handling good/bad and how you deal with errors would be a fantastic video! :) pls :)
@SusilVignesh
@SusilVignesh 3 жыл бұрын
My man!! Thank you :-)
@fufuhu148
@fufuhu148 6 жыл бұрын
Hi man, I think you are such a crusher in C++. I did a C++ course called Advanced C++ programming at my Uni few years ago, but I couldn't link the knowledge together back in the day. After watching your video series, things start making sense to me now.
@fufuhu148
@fufuhu148 6 жыл бұрын
That's why i decide to become on a partner supporter on Patreon
@pooria_garrett3020
@pooria_garrett3020 6 жыл бұрын
Hey is there any reason Cherno hasn't uploaded in more than 10 days? :(
@fufuhu148
@fufuhu148 6 жыл бұрын
I have no idea
@mikedoeren5960
@mikedoeren5960 Жыл бұрын
Great to know - thanks!
@femloh
@femloh Жыл бұрын
Simple. I get it now. Thanks Cherno.
@abhisheksa6635
@abhisheksa6635 11 ай бұрын
Awesome
@pfever
@pfever Жыл бұрын
All these C++ "features" that programmers need to learn, seem more like C++ bugs that we need to patch lol
@Llonerin2
@Llonerin2 5 жыл бұрын
Lol @ the subtitles here: "Hey look guys my name is Jenna..."
@eyalpery8470
@eyalpery8470 4 жыл бұрын
Thanks!
@eshaanrathi4658
@eshaanrathi4658 6 жыл бұрын
thanks a lot sir
@tjalferes
@tjalferes 2 жыл бұрын
thanks
@mallasarjsulebhanvi4128
@mallasarjsulebhanvi4128 2 жыл бұрын
Thsnks
@user-ch2hk9pf4f
@user-ch2hk9pf4f 6 жыл бұрын
Hi. Will there be a series with creating a game engine with your great explanations?) Thanks
@giusepperandazzo5357
@giusepperandazzo5357 2 жыл бұрын
great content! how these concepts are shifted into the smart pointers? I actually got that smart pointers, delete objects by themself when program doesn't need anymore.
@alfonsogonzalez-astrodynam2207
@alfonsogonzalez-astrodynam2207 3 жыл бұрын
Great explanation! thank you
@l6e6i6n
@l6e6i6n 3 жыл бұрын
i never knew this could happen
@nandhakumarmurugan9923
@nandhakumarmurugan9923 4 жыл бұрын
Can we expect a video on detailed explanation on VTable and Vptr from you soon, pls.
@rajakishan1
@rajakishan1 4 жыл бұрын
are you planning to do videos on data structures and algorithms ??
@Nuclearblastdrone
@Nuclearblastdrone 5 жыл бұрын
He is talking right. Give him beer
@KingCitaldo125
@KingCitaldo125 3 жыл бұрын
A good tip is that you can always declare your class' destructors as virtual. This doesn't have any adverse effect for a class that does not have any child classes, but it will help if there are classes in the future that have to be derived from a base. Also worth mentioning that the Base class' virtual destructor will carry over regardless of the number of generations that are created(Parent class becomes a Grandparent, so-to-speak, or Great-Grandparent, so on..): The children and grandchildren all will still have their own respective destructors called as long as, at the very least, the 1st generation has a virtual destructor.
@jurakpreetsingh2155
@jurakpreetsingh2155 2 жыл бұрын
Can u code in c++ now?
@wang_7665
@wang_7665 Жыл бұрын
How is the memory cost? As far as I know, a virtual function will incur a virtual pointer.
@oceanexplorationlab
@oceanexplorationlab Жыл бұрын
This series is excellent! Does anybody know what the performance cost is of declaring destructors as virtual?
@yardencohen9521
@yardencohen9521 3 жыл бұрын
Just an FYI, the location of the video cards (suggesting to jump to previous videos you made) is on the right for people using LTR language on KZfaq (like English), and on the left for people using RTL (like Arabic)
@Yupppi
@Yupppi 9 ай бұрын
I feel like this is the first time we touched deleting classes in all the class videos. Why so? When and when not to?
@varunduke
@varunduke 3 жыл бұрын
When a derived class calls its constructor does the compiler implicitly call the base class constructor as well? In the example after calling the constructor of derived class it seemed to call the base class constructor first even when the destuctor of the base class was called in a statement before.
@huseyinsencan8627
@huseyinsencan8627 6 жыл бұрын
Hey Cherno, we are waiting for static polymorphism video
@haxterhuz8346
@haxterhuz8346 6 жыл бұрын
I'm not joking but Yan looks like Chris Pratt
@a2p75
@a2p75 2 жыл бұрын
When using std::shared_ptr poly = std::make_shared(), my program called BOTH the derived AND base destructor when exiting the scope even though I hadn't marked ~Base() as virtual (windows 7, mingw compiler). Is it something shared_pointer does, is it something that make_shared does, or is it something I completely missed?
@siddharthchauhan1129
@siddharthchauhan1129 4 жыл бұрын
I am very confused how vptr and vtable works. Please make a video on that too.
@bloodwolf8307
@bloodwolf8307 4 жыл бұрын
cool
@julianelischer
@julianelischer 4 жыл бұрын
what happens if you put the virtual on the derived destructor?
@k-tech2937
@k-tech2937 3 жыл бұрын
Wow, I have neve realized how many memory leaks i made lol
@smileynetsmileynet7922
@smileynetsmileynet7922 3 жыл бұрын
If u could make a video on types of bugs that can be present, that you have seen, that would be great. Run time bugs, I mean.
@smileynetsmileynet7922
@smileynetsmileynet7922 3 жыл бұрын
For example, right now my PNF_Object2 crashes sometime when I create an object of that type. Thats what I'm working on right now.
@rob-890
@rob-890 6 жыл бұрын
You said previously that you work at EA on core technology I'm curious what engine are you actively developing. Frostbite?
@Keltheran
@Keltheran 6 жыл бұрын
He works on EA in Australia(Firemonkeys Studios) so it is not Frostbite (since that is made by DICE in Sweden)
@zf0666
@zf0666 6 жыл бұрын
Mobile engine
@PuneetKumar-mz9is
@PuneetKumar-mz9is 5 жыл бұрын
what is the use of pure virtual function?
@vbessonow
@vbessonow 3 жыл бұрын
when you need to do something like that Base* poly = new Derived();? why you you need instance of Base that is created with Derived?
@kindpotato
@kindpotato 4 жыл бұрын
This guy types so FAST
@soumyadeepdas1199
@soumyadeepdas1199 Жыл бұрын
what happens if we use pure virtual destructor?
@jcfb9527
@jcfb9527 4 жыл бұрын
Why the base constructor does not need to be virtual?
@cedricngoran7045
@cedricngoran7045 6 жыл бұрын
Is the video at a normal speed ?
@ghipsandrew
@ghipsandrew 6 жыл бұрын
Why would we then not use virtual destructors every time? Why isn't this the default ?
@loia5tqd001
@loia5tqd001 6 жыл бұрын
As far as I know, The classes that have "virtual" (any virtual keyword) will need a pointer to point to a vtable. It costs 1 vtable each class and a pointer for each instance. You can try sizeof(ClassHasVirtual) and sizeof(ClassDoesntHaveVirtual) to see the different. Sorry for my bad English. It's just an idea. You can google for more information.
@nikoszervo
@nikoszervo 6 жыл бұрын
c++ babe!!!
@feraudyh
@feraudyh 6 жыл бұрын
How about this: you could cast your pointer to the derived class and call the derived class destructor if you really want to save on memory used by vtables. Any comments?
@BlackJar72
@BlackJar72 6 жыл бұрын
The vtable not only requires an extra pointer but has a (very small) amount of overhead to look the correct function version up. Actually, commonly accepted best practices will say to always make the destructor virtual (and some IDEs will warn you if you don't). But C++ likes to give you power to choose as you like -- so, say, a small vec3f class (or a struct) might not need one *if* you know you will never extend it. C++ leave the option, just in case (unlike, say, Java, where all methods are inherently virtual).
@BlackJar72
@BlackJar72 6 жыл бұрын
Henri de Feraudy: The problem is that, is many applications, the type will be determined at run time, and you won't always know what type it is before hand. Also, there is the danger you might foreget, or someone else will work on the code. For example, you create a type entity, and derived types zombie and skeleton, with different data. You store all the current entities in a game in a vector as the spawn. When they despawn they are removed from the vector and the destructor is called. A loop that runs on all entities in a the vector doesn't know if a given entity is a skeleton or a zombie.
@yonlehman
@yonlehman 4 жыл бұрын
Why can't the compiler warn you when you are inheriting from a class without a virtual destructor?
@redon4
@redon4 6 жыл бұрын
nice video cherno , i have a question : can we call the superclass constructor from base class like in java super.(); thanks for the video.
@jamesmnguyen
@jamesmnguyen 6 жыл бұрын
Yes. class Base { public: Base(int bla) : m_bla(bla) {} private: int m_bla: }; class Derived : public Base { public: Derived() : Base(5) {} }; Note: The Base constructor must be called in the member initialization list. Calling Base in the constructor body does nothing except create a Base instance which gets destroyed at the end of the scope.
@LucyAndLily502
@LucyAndLily502 6 ай бұрын
why treat sub class pointer as base class pointer?
@UchihaNarutoFTW
@UchihaNarutoFTW 2 ай бұрын
Why we don't need virtual constructor though? Might be a dumb question, but thanks for any reply.
@koobyroory
@koobyroory 6 жыл бұрын
What IDE do you use? it looks like something I'd want to try out.
@nickpappas6109
@nickpappas6109 6 жыл бұрын
King Luke it’s visual studio
@v0idptr
@v0idptr 6 жыл бұрын
Visual Studio
@koobyroory
@koobyroory 6 жыл бұрын
Oh haha, thanks guys!
@eduardovaldez4013
@eduardovaldez4013 2 жыл бұрын
Quick question though, why would you create a pointer to the base class but then make it point to the memory address of a derived class? Seems to me like you can avoid the problem if you simply create the appropriate pointer to begin with. Is there another reason why'd you'd want to use a base class pointer to point to a derived class?
@heylittleguy26
@heylittleguy26 2 жыл бұрын
For instances where you don't necessarily know if it is Base or Derived. For example, if you have an entity class with a move method, with a derived player class, you might want all entities in the scene to update inside of a loop. So you might create an array of entities and assign one of the positions of the array to the player, as an example
@maciej12345678
@maciej12345678 2 жыл бұрын
7:01 why this is already build in compiler?
@mixedbytc
@mixedbytc Жыл бұрын
I'd recommend not using the term "sub-class" because it's not a part of the specification and creates ambiguity between *derived classes* and *nested classes.*
@alexandruandricsak4051
@alexandruandricsak4051 6 жыл бұрын
Can you make a video about linked lists or binary trees? By the way keep up the good work, you're very good at this!
@peterarbeitsloser7819
@peterarbeitsloser7819 3 жыл бұрын
Can someone please explain why we have to put a "public" after the ":"?
@peterarbeitsloser7819
@peterarbeitsloser7819 3 жыл бұрын
@Peterolen wow, okay. I can't really find any usecase for such a privately inheriting class though
@ajj7794
@ajj7794 4 жыл бұрын
what does base* poly = new derived mean? why ever does this? from my understanding the base tells the compiler how much memory is required. but since derived calsses will have more variable and stuff than base, this will result in the pointer base having more memory than what it thinks is required. i hope that made sense. like assume base has 1 int which is 4 bytes thus a point to base will have 4 bytes at the address of base. if i say base pointer and give it a pointer of a derived now it still thinks that it has 4 bytes but the derived might have 2 ints and thus 8 bytes.
@ajj7794
@ajj7794 4 жыл бұрын
@Peterolen thank you for this, this helped med in understanding. i now understand that the base class can store the pointers to the deriaved classes but you have to be very cafull and know what each deriaved class is. Else you would confuse the deriaved classes and if u assume that the class a is what u are hold the pointer to but in reality it is class but then upon calling a class a memeber or function class b would return and error as they dont exist and the program would crash.
@reverse5461
@reverse5461 6 жыл бұрын
You can see that he is aging if you compare him with the first video
@zielony1212
@zielony1212 3 жыл бұрын
why to use x * y = new y() instead of x y()?
@chiyungchu9463
@chiyungchu9463 3 жыл бұрын
+1
@jurakpreetsingh2155
@jurakpreetsingh2155 2 жыл бұрын
@@chiyungchu9463 hey man,can you code in c++?
@xristosbart8217
@xristosbart8217 6 жыл бұрын
If we have 3 classes A,B,C. A is the master class, B inherits from A and C inherits from B. The only Virtual Constructor Is A’s. If I treat a C instance as an A instance all three constructors are called or only A, B? And a second question if I treat the C instance as a B instance only the B constructor is called right?
@valiok9880
@valiok9880 6 жыл бұрын
I am not 100% sure I understood you correctly, but constructors are always called in the hierarchical "tree" of inheritance. I do not hink there is something like a virtual constructor, there are only virtual destructors. In both cases the 3 constructs will be called in the order: A,B,C. In your second example, all 3 are called again, since B inherits from A. Nonetheless you can always try it
@xristosbart8217
@xristosbart8217 6 жыл бұрын
Oh my mistake I was talking about destructors not constructors
@xristosbart8217
@xristosbart8217 6 жыл бұрын
Valiok 98 I meant virtual destructors
@valiok9880
@valiok9880 6 жыл бұрын
I just tested it out and in both cases all 3 constructors and all 3 destructors were called, whereas I declared only the destructor of A as virtual. If you think about it , it makes sense, because virtual basically means that the method can be overridden. So the B destructor calls the destructor of A. Since the object is of type C it must go up the tree through B and then to A, which in a sense "overrides" B's destructor which overrides A's destructor because it was declared virtual. But better check the c++ documentation or ask someone more sophisticated
@xristosbart8217
@xristosbart8217 6 жыл бұрын
Valiok 98 Thanks ever so much
@aleksandarstanisic1848
@aleksandarstanisic1848 6 жыл бұрын
uff more leaks ]
@earthserenityforever
@earthserenityforever 6 жыл бұрын
what is the software/hardware you use to record these tutorials :)
@AbhishekKumar1902
@AbhishekKumar1902 5 жыл бұрын
I am new to C++ and OOPS, can somebody please explain what is the use case of when you do: Base* poly = new Derived();
@GfastGao
@GfastGao 5 жыл бұрын
2:05 :(on Line 10) "lass Derived : public Base" Question: Why do we need specify "public" for base class? Any reason?
@boltgreywing1718
@boltgreywing1718 6 жыл бұрын
Hey cherno can you do help me understand the tile coordinate system from game programming episode 62? I understand the x and y used in the constructor but not the r function and the tile size parameter. Can you please explain in depth how to use them? I am wanting to use them in a platformer type game but I want to understand really what they do better. Also the game I am trying to develop uses 4by4, 8by8, 16by16, and 32by32 tiles respectively. Unfortunately the way the program that you built runs only operates on 16by16 tiles. I want a different one that can operate on any type of sprite size no matter what I throw into it.
@swagluke2370
@swagluke2370 6 жыл бұрын
TheChernoProject, could you please tell me how you make game engines in EA, I think you said you made them in C#. How? I can't find many good ways to write a game enigine in C#. in fact this is why im learning C++.
@vixellar7933
@vixellar7933 6 жыл бұрын
The core engine is made in c++ and the tools used in engine is made with c#
@swagluke2370
@swagluke2370 6 жыл бұрын
Yes, but how would I get a screen and render it in C#? I mean these all kinds of SDL SFML and the list goes on for C++. but C#? I cant find nothing like that in C#.
@swagluke2370
@swagluke2370 6 жыл бұрын
Thank you for your response. that makes a lot of sense now.
@swagluke2370
@swagluke2370 6 жыл бұрын
Thanks for telling me what should be in an engine! i kind of needed that
@someguy2910
@someguy2910 5 жыл бұрын
"Whenever your writing a class that you will be extending or that might be sub-classed you need to declare your destructor as 'virtual' otherwise no one is going to be able to safely extend that class including yourself because if you do that you can't use the destructor because it will never get called if you're treating that class by it's base type which might be the case if your passing it into a function as well and maybe that function only takes it as a base pointer and then deletes it or does whatever it does. This is an example anyway so definitely make sure that you are declaring the destructors 'virtual' if your allowing that sub-class to actually happen." - Cherno
@TheLordoftheDarkness
@TheLordoftheDarkness 3 жыл бұрын
you're*
@swagluke2370
@swagluke2370 6 жыл бұрын
Help, i have a function in a class: class Logging { public: void Log(const char* text) { std::cout
@swagluke2370
@swagluke2370 6 жыл бұрын
What if i wanted to do Log("FPS: " + fps); // Fps doesnt exist but just an example
@swagluke2370
@swagluke2370 6 жыл бұрын
thanks
@JohannesEckhoff
@JohannesEckhoff 6 жыл бұрын
I don't really understand why the base constructor is called when a derived object is constructed. Can someone ELI5?
@Djzaamir
@Djzaamir 6 жыл бұрын
Imagine if the base class need's to do some memory allocation's or setup some stuff, before it can be utilized, that's why base constructor is called First
@JohannesEckhoff
@JohannesEckhoff 6 жыл бұрын
Thank you.
@shorakie5068
@shorakie5068 6 жыл бұрын
when will the Game Engine Tutorial series began ??
@custard131
@custard131 6 жыл бұрын
why is 'virtual' not the default state like it is in other languages?
@TheLordoftheDarkness
@TheLordoftheDarkness 3 жыл бұрын
Because it adds overhead on both the cpu and memory.
@tritranminh4990
@tritranminh4990 5 жыл бұрын
So, should I make every destructor virtual just to be safe?
@TheCherno
@TheCherno 5 жыл бұрын
Not for classes that will never be subclassed, since you don't want to add the overhead of a vtable if you don't need to. If you ever have any virtual functions in your class then yes, always. Note that you of course do not need to have virtual functions to require a virtual destructor, since virtual desctructors should be used on every class that is subclassed.
@mateoivanov4998
@mateoivanov4998 6 жыл бұрын
How much time did you need to learn C++ and Java...(You are programming for 7 Years but how much Time did you need to learn this two Languages ?)
@SimplySpiceIt
@SimplySpiceIt 6 жыл бұрын
Second comment:
@vertigo6982
@vertigo6982 5 жыл бұрын
Representing Second place... The first loser.. I like your style. Keep up the good work.
@astrodot1503
@astrodot1503 4 жыл бұрын
I hate OOP 😭, dunno what im gonna DO... IT'S HARD IT'S HARD IT'S HARD . It doesn't make sense it should not exists It's ruining everything in my life😭😭😭
@mateoivanov4998
@mateoivanov4998 6 жыл бұрын
How much time did you need to learn C++ and Java...(You are programming for 7 Years but how much Time did you need to learn this two Languages ?)
Casting in C++
13:25
The Cherno
Рет қаралды 183 М.
Macros in C++
19:36
The Cherno
Рет қаралды 234 М.
Pleased the disabled person! #shorts
00:43
Dimon Markov
Рет қаралды 30 МЛН
Опасность фирменной зарядки Apple
00:57
SuperCrastan
Рет қаралды 12 МЛН
1,000 Diamonds! (Funny Minecraft Animation) #shorts #cartoon
00:31
toonz CRAFT
Рет қаралды 39 МЛН
SINGLETONS in C++
19:16
The Cherno
Рет қаралды 197 М.
Precompiled Headers in C++
21:30
The Cherno
Рет қаралды 154 М.
lvalues and rvalues in C++
14:13
The Cherno
Рет қаралды 308 М.
are "smart pointers" actually smart?
9:44
Low Level Learning
Рет қаралды 72 М.
Type Punning in C++
13:20
The Cherno
Рет қаралды 155 М.
How C++ took a turn for the worse
5:03
Code Persist
Рет қаралды 268 М.
Arrays in C++
18:31
The Cherno
Рет қаралды 419 М.
Track MEMORY ALLOCATIONS the Easy Way in C++
13:25
The Cherno
Рет қаралды 160 М.
Bitwise AND (&), OR (|), XOR (^) and NOT (~) in C++
20:27
The Cherno
Рет қаралды 69 М.
Pleased the disabled person! #shorts
00:43
Dimon Markov
Рет қаралды 30 МЛН