C++ 11 新特性之Class

一根笨茄子發表於2016-09-08

這是C++11新特性介紹的第六部分,涉及到Class的相關內容。

不想看toy code的讀者可以直接拉到文章最後看這部分的總結。

sizeof

新標準中,sizeof可以直接用於獲取Class::member的大小,而不用通過Class的例項。

class TestClass
{
public:
	int member_i;
	char member_c;
};

cout<<"test sizeof class member:\n";
cout<<sizeof(TestClass::member_i)<<"\t"<<sizeof(TestClass::member_c)<<endl;

default constructor

新標準中,可以通過=default強制編譯器生成一個預設constructor。

class TestClass
{
public:
	TestClass() = default;
	TestClass(const int i, const char c): member_i(i), member_c(c) {}

	int member_i;
	char member_c;
};

cout<<"test =default class construct:\n";
TestClass tc; // may cause error if no default construct.
cout<<tc.member_i<<'\t'<<(short)tc.member_c<<endl;
cout<<"test =default done."<<endl;

在上面的程式碼中,如果我們不提供預設constructor的話,無法通過TestClass tc定義一個例項。

delegate constructor

新標準中,可以在初始化列表中將一個constructor初始化的工作委託給另一個constructor。

class TestClass
{
public:
	TestClass() = default;
	TestClass(const int i, const char c): member_i(i), member_c(c) {}
	TestClass(const int i): TestClass(i, 0) { member_c = 'T';}

	int member_i;
	char member_c;
};

cout<<"test delegating constructor:\n";
TestClass tc2(2);
cout<<tc2.member_i<<'\t'<<tc2.member_c<<endl;

allocator.construct

新標準中,allocator.construct可以使用任意的建構函式。

class TestClass
{
public:
	TestClass() = default;
	TestClass(const int i, const char c): member_i(i), member_c(c) {}
	TestClass(const int i): TestClass(i, 0) { member_c = 'T';}

	int member_i;
	char member_c;
};

cout<<"test allocator:\n";
allocator<TestClass> alloc;
auto p = alloc.allocate(10);
alloc.construct(p, 10);
cout<<p->member_i<<'\t'<<p->member_c<<endl;

copy constructor

新標準中,可以通過=default要求編譯器合成預設的拷貝/賦值建構函式。

class TestClass
{
public:
	TestClass() = default;
	TestClass(const int i, const char c): member_i(i), member_c(c) {}
	TestClass(const int i): TestClass(i, 0) { member_c = 'T';}
	TestClass(const TestClass&) = default;
	TestClass& operator=(const TestClass&);

	int member_i;
	char member_c;
};

cout<<"test =default class copy construct:\n";
TestClass tc3(tc2);
TestClass tc4 = tc2;
cout<<tc3.member_i<<'\t'<<tc3.member_c<<endl;
cout<<tc4.member_i<<'\t'<<tc4.member_c<<endl;

同樣,新標準中也允許用=delete禁止拷貝。

class TestClass
{
public:
	TestClass() = default;
	TestClass(const int i, const char c): member_i(i), member_c(c) {}
	TestClass(const int i): TestClass(i, 0) { member_c = 'T';}
	TestClass(const TestClass&) = delete;
	TestClass& operator=(const TestClass&);

	int member_i;
	char member_c;
};
TestClass& TestClass::operator=(const TestClass&) = default;

cout<<"test =delete class copy construct:\n";
//TestClass tc5(tc2); // error: use of deleted function ‘TestClass::TestClass(const TestClass&)’
//cout<<tc5.member_i<<'\t'<<tc5.member_c<<endl;
cout<<"test =delete done."<<endl;

override和final

新標準中提供了override和final兩個關鍵字,用於標識子類對父類中虛擬函式的重寫(override)或禁止重寫(final)。

class TestClass
{
public:
	TestClass() = default;
	TestClass(const int i, const char c): member_i(i), member_c(c) {}
	TestClass(const int i): TestClass(i, 0) { member_c = 'T';}
	TestClass(const TestClass&) = default;
	TestClass& operator=(const TestClass&);

	virtual void print_msg() {cout<<member_i<<'\t'<<member_c<<endl;}
	virtual void final_foo() final {}

	int member_i;
	char member_c;
};
TestClass& TestClass::operator=(const TestClass&) = default;

class SubTestClass final: public TestClass
{
	public:
		using TestClass::TestClass;
		SubTestClass(const int i): TestClass(i, 'S') {}
		void print_msg() override;
		//void print_msg(char c) override;
		//‘void SubTestClass::print_msg(char)’ marked override, but does not override

		//void final_foo() {}
		//overriding final function ‘virtual void TestClass::final_foo()’
};
//class SubSubTestClass: public SubTestClass {}; 
// cannot derive from ‘final’ base ‘SubTestClass’ in derived type ‘SubSubTestClass’

void SubTestClass::print_msg() 
{
	cout<<"i: "<<member_i<<'\t'<<"c: "<<member_c<<endl;
}

cout<<"test override:\n";
TestClass *stc_ptr = new SubTestClass(10); 
stc_ptr->print_msg();
SubTestClass stc(10);
TestClass tc6 = (TestClass)stc;
tc6.print_msg();

如果標識了override的函式實際上沒有重寫父類中的函式,或者標識final的函式被子類重寫,編譯器都會報錯。

通樣的,標識為final的類也不允許作為父類被繼承。

委託父類建構函式

新標準中,也支援子類在初始化列表中直接委託父類的建構函式完成初始化。

class SubTestClass final: public TestClass
{
	public:
		using TestClass::TestClass;
		SubTestClass(const int i): TestClass(i, 'S') {}
		void print_msg() override;
};

cout<<"test inherit base class contructor:\n";
SubTestClass stc2(1024, 'H');
stc2.print_msg();

多繼承與預設constructor

多重繼承的子類可以直接繼承父類的建構函式,但是如果父類中有形參列表完全相同的建構函式,則會產生衝突,這時需要子類自己定義一個自己版本的建構函式。

class TestClass2
{
public:
	TestClass2() = default;
	TestClass2(const int i) {}
};

class MultiSubClass: public TestClass, public TestClass2
{
public:
	using TestClass::TestClass;
	using TestClass2::TestClass2;
	// conflicts with version inherited from ‘TestClass’
	MultiSubClass(const int i): TestClass(i) {}
	MultiSubClass() = default;
};

cout<<"test multi inherit constructor:\n";
MultiSubClass mtc(1024);
mtc.print_msg();
return 0;

總結

  1. sizeof可以直接用於獲取Class::member的大小,而不用通過Class的例項。
  2. 可以通過=default強制編譯器生成一個預設constructor。
  3. 可以在初始化列表中將一個constructor初始化的工作委託給另一個constructor,以及父類的constructor。
  4. allocator.construct可以使用任意的建構函式。
  5. 可以通過=default要求編譯器合成預設的拷貝/賦值建構函式,也可以通過=delete禁止拷貝。
  6. 新標準中提供了override和final兩個關鍵字,用於標識子類對父類中虛擬函式的重寫(override)或禁止重寫(final),編譯會對這兩種情況進行檢查。final還可以用於類的標識,表示禁止繼承。
  7. 多重繼承的子類可以直接繼承父類的建構函式,但是如果父類中有形參列表完全相同的建構函式,則會產生衝突,這時需要子類自己定義一個自己版本的建構函式。

完整程式碼詳見class.cpp

相關文章