在專注於構建/破壞/複製(以及最終優化)的動態之前,有一個您似乎沒有意識到的問題:值不是多態。
如果B
從A
派生,
B b;
A a(b);
不會讓a
的b
副本。它只會複製a
b
的A
子組件。
不同的價值觀,指針和引用是多態:
B b;
B* pb = &b;
A* pa = pb;
B* pb2 = const_cast<B*>(pa);
實際上將導致PA指向B的子組件,但pb
和pb2
指向同一b
。
這就是說,一個vector<A>
包含values
,因此
vecotr<A> v;
v.push_back(B());
將導致:
- 創建一個空
v
;
- 創建一個臨時B();
- 使v大到足以包含A
- 在v.end()創建一個從臨時B的A子組件複製的A.
- 銷燬臨時乙
及 - 功能的結束時,
存儲器是現在清潔。
如果使用指針:
vector<A*> v;
v.push_back(new B());
將導致:
- 創建一個空
v
- 在堆上創建乙
- 放大v到包含A *
- 將B的地址轉換爲其A的子組件地址(對於單一繼承,它們將最可能是相同的)
- 在v.end()創建一個從B的轉換後的地址複製的A *(注意你正在轉換指針,而不是對象)。
- 毀壞v
- 銷燬其中的A *。
- 堆上的B是仍然存在(內存泄漏,因爲沒有其他的方式來訪問它來刪除它)
爲了避免泄露你應該:
- 創建乙在堆棧中,並得到它的地址或...
- 在向量中使用
std::unique_ptr<A>
而不是A*
(以便在向量銷燬時,unique_ptr被銷燬並且其析構函數銷燬指向的A子對象,虛擬析構函數會導致B的破壞。
一個以上問題的更有效的示範可以通過下面的代碼給出:
// Compile as g++ -pedantic -Wall -std=c++11
#include <vector>
#include <list>
#include <iostream>
class A
{
public:
A() { std::cout << "- creating A at " << this << std::endl; }
A(const A& a) { std::cout << "- creating A at " << this << " from " << &a << std::endl; }
A& operator=(const A& a) { std::cout << "- assigning A at " << this << " from " << &a << std::endl; return *this; }
virtual ~A() { std::cout << "- destroying A at " << this << std::endl; }
virtual void hello() const { std::cout << "- A's hello from " << this << std::endl; }
};
class B: public A
{
public:
B() { std::cout << "- creating B at " << this << std::endl; }
B(const B& a) { std::cout << "- creating B at " << this << " from " << &a << std::endl; }
B& operator=(const B& a) { std::cout << "- assigning B at " << this << " from " << &a << std::endl; return *this; }
virtual ~B() { std::cout << "- destroying B at " << this << std::endl; }
virtual void hello() const { std::cout << "- B's hello from " << this << std::endl; }
};
class C: public A
{
public:
C() { std::cout << "- creating C at " << this << std::endl; }
C(const C& a) { std::cout << "- creating C at " << this << " from " << &a << std::endl; }
C& operator=(const C& a) { std::cout << "- assigning C at " << this << " from " << &a << std::endl; return *this; }
virtual ~C() { std::cout << "- destroying C at " << this << std::endl; }
virtual void hello() const { std::cout << "- C's hello from " << this << std::endl; }
};
int main()
{
std::cout << "creating some objects" << std::endl;
A a1, a2;
B b1, b2;
C c1, c2;
{
std::cout << "operating with values" << std::endl;
std::vector<A> valvect;
valvect.push_back(a1);
valvect.push_back(a1);
valvect.push_back(b1);
valvect.push_back(b1);
valvect.push_back(c1);
valvect.push_back(c1);
valvect.push_back(a2);
valvect.push_back(a2);
valvect.push_back(b2);
valvect.push_back(b2);
valvect.push_back(c2);
valvect.push_back(c2);
for(const auto& x: valvect) x.hello();
std::cout << "at '}' destroy the value vector" << std::endl;
}
{
std::cout << "operating with pointers" << std::endl;
std::vector<A*> ptrvect;
ptrvect.push_back(&a1);
ptrvect.push_back(&a1);
ptrvect.push_back(&b1);
ptrvect.push_back(&b1);
ptrvect.push_back(&c1);
ptrvect.push_back(&c1);
ptrvect.push_back(&a2);
ptrvect.push_back(&a2);
ptrvect.push_back(&b2);
ptrvect.push_back(&b2);
ptrvect.push_back(&c2);
ptrvect.push_back(&c2);
for(const auto& x: ptrvect)
x->hello();
std::cout << "at '}' destroy the pointer's vector" << std::endl;
}
{
std::cout << "operating with list of values" << std::endl;
std::list<A> vallst;
vallst.push_back(a1);
vallst.push_back(a1);
vallst.push_back(b1);
vallst.push_back(b1);
vallst.push_back(c1);
vallst.push_back(c1);
vallst.push_back(a2);
vallst.push_back(a2);
vallst.push_back(b2);
vallst.push_back(b2);
vallst.push_back(c2);
vallst.push_back(c2);
for(const auto& x: vallst)
x.hello();
std::cout << "at '}' destroy the value list" << std::endl;
}
{
std::cout << "operating with list of pointers" << std::endl;
std::list<A*> ptrlst;
ptrlst.push_back(&a1);
ptrlst.push_back(&a1);
ptrlst.push_back(&b1);
ptrlst.push_back(&b1);
ptrlst.push_back(&c1);
ptrlst.push_back(&c1);
ptrlst.push_back(&a2);
ptrlst.push_back(&a2);
ptrlst.push_back(&b2);
ptrlst.push_back(&b2);
ptrlst.push_back(&c2);
ptrlst.push_back(&c2);
for(const auto& x: ptrlst)
x->hello();
std::cout << "at '}' destroy the pointer's list" << std::endl;
}
std::cout << "now finally at '};' destroy the objects created at the beginning" << std::endl;
return 0;
}
它會輸出像
creating some objects
- creating A at 0x22febc
- creating A at 0x22feb8
- creating A at 0x22feb4
- creating B at 0x22feb4
- creating A at 0x22feb0
- creating B at 0x22feb0
- creating A at 0x22feac
- creating C at 0x22feac
- creating A at 0x22fea8
- creating C at 0x22fea8
operating with values
- creating A at 0x3e3eb8 from 0x22febc
- creating A at 0x3e2434 from 0x22febc
- creating A at 0x3e2430 from 0x3e3eb8
- destroying A at 0x3e3eb8
- creating A at 0x3e2448 from 0x22feb4
- creating A at 0x3e2440 from 0x3e2430
- creating A at 0x3e2444 from 0x3e2434
- destroying A at 0x3e2430
- destroying A at 0x3e2434
- creating A at 0x3e244c from 0x22feb4
- creating A at 0x3e2468 from 0x22feac
- creating A at 0x3e2458 from 0x3e2440
- creating A at 0x3e245c from 0x3e2444
- creating A at 0x3e2460 from 0x3e2448
- creating A at 0x3e2464 from 0x3e244c
- destroying A at 0x3e2440
- destroying A at 0x3e2444
- destroying A at 0x3e2448
- destroying A at 0x3e244c
- creating A at 0x3e246c from 0x22feac
- creating A at 0x3e2470 from 0x22feb8
- creating A at 0x3e2474 from 0x22feb8
- creating A at 0x3e24a0 from 0x22feb0
- creating A at 0x3e2480 from 0x3e2458
- creating A at 0x3e2484 from 0x3e245c
- creating A at 0x3e2488 from 0x3e2460
- creating A at 0x3e248c from 0x3e2464
- creating A at 0x3e2490 from 0x3e2468
- creating A at 0x3e2494 from 0x3e246c
- creating A at 0x3e2498 from 0x3e2470
- creating A at 0x3e249c from 0x3e2474
- destroying A at 0x3e2458
- destroying A at 0x3e245c
- destroying A at 0x3e2460
- destroying A at 0x3e2464
- destroying A at 0x3e2468
- destroying A at 0x3e246c
- destroying A at 0x3e2470
- destroying A at 0x3e2474
- creating A at 0x3e24a4 from 0x22feb0
- creating A at 0x3e24a8 from 0x22fea8
- creating A at 0x3e24ac from 0x22fea8
- A's hello from 0x3e2480
- A's hello from 0x3e2484
- A's hello from 0x3e2488
- A's hello from 0x3e248c
- A's hello from 0x3e2490
- A's hello from 0x3e2494
- A's hello from 0x3e2498
- A's hello from 0x3e249c
- A's hello from 0x3e24a0
- A's hello from 0x3e24a4
- A's hello from 0x3e24a8
- A's hello from 0x3e24ac
at '}' destroy the value vector
- destroying A at 0x3e2480
- destroying A at 0x3e2484
- destroying A at 0x3e2488
- destroying A at 0x3e248c
- destroying A at 0x3e2490
- destroying A at 0x3e2494
- destroying A at 0x3e2498
- destroying A at 0x3e249c
- destroying A at 0x3e24a0
- destroying A at 0x3e24a4
- destroying A at 0x3e24a8
- destroying A at 0x3e24ac
operating with pointers
- A's hello from 0x22febc
- A's hello from 0x22febc
- B's hello from 0x22feb4
- B's hello from 0x22feb4
- C's hello from 0x22feac
- C's hello from 0x22feac
- A's hello from 0x22feb8
- A's hello from 0x22feb8
- B's hello from 0x22feb0
- B's hello from 0x22feb0
- C's hello from 0x22fea8
- C's hello from 0x22fea8
at '}' destroy the pointer's vector
operating with list of values
- creating A at 0x3e2448 from 0x22febc
- creating A at 0x3e24d0 from 0x22febc
- creating A at 0x3e24e8 from 0x22feb4
- creating A at 0x3e2500 from 0x22feb4
- creating A at 0x3e2518 from 0x22feac
- creating A at 0x3e2530 from 0x22feac
- creating A at 0x3e2548 from 0x22feb8
- creating A at 0x3e2560 from 0x22feb8
- creating A at 0x3e2578 from 0x22feb0
- creating A at 0x3e2590 from 0x22feb0
- creating A at 0x3e25a8 from 0x22fea8
- creating A at 0x3e25c0 from 0x22fea8
- A's hello from 0x3e2448
- A's hello from 0x3e24d0
- A's hello from 0x3e24e8
- A's hello from 0x3e2500
- A's hello from 0x3e2518
- A's hello from 0x3e2530
- A's hello from 0x3e2548
- A's hello from 0x3e2560
- A's hello from 0x3e2578
- A's hello from 0x3e2590
- A's hello from 0x3e25a8
- A's hello from 0x3e25c0
at '}' destroy the value list
- destroying A at 0x3e2448
- destroying A at 0x3e24d0
- destroying A at 0x3e24e8
- destroying A at 0x3e2500
- destroying A at 0x3e2518
- destroying A at 0x3e2530
- destroying A at 0x3e2548
- destroying A at 0x3e2560
- destroying A at 0x3e2578
- destroying A at 0x3e2590
- destroying A at 0x3e25a8
- destroying A at 0x3e25c0
operating with list of pointers
- A's hello from 0x22febc
- A's hello from 0x22febc
- B's hello from 0x22feb4
- B's hello from 0x22feb4
- C's hello from 0x22feac
- C's hello from 0x22feac
- A's hello from 0x22feb8
- A's hello from 0x22feb8
- B's hello from 0x22feb0
- B's hello from 0x22feb0
- C's hello from 0x22fea8
- C's hello from 0x22fea8
at '}' destroy the pointer's list
now finally at '};' destroy the objects created at the beginning
- destroying C at 0x22fea8
- destroying A at 0x22fea8
- destroying C at 0x22feac
- destroying A at 0x22feac
- destroying B at 0x22feb0
- destroying A at 0x22feb0
- destroying B at 0x22feb4
- destroying A at 0x22feb4
- destroying A at 0x22feb8
- destroying A at 0x22febc
我相信這是最好的答案,當你在頂部說明這些值不是多態的,而且它只是複製結構使它看起來如此,你對每種類型的矢量所經歷的過程的解釋都很容易理解並且相當徹底。 – FatalCatharsis