1. Big Three
class String
{
public:
String (const char* cstr=0);
String (const String &str);
String &operator=(const String &str);
char* get_c_str() const {return m_data;}
private:
char *m_data;
};
構造函數 析構函數
inline
String::String(const char*cstr = 0)
{
if (cstr) {
m_data = new char[strlen(cstr)+1]; //+1是不要忘了結尾的'\0',strlen是不計算'\0'的
strcpy(m_data, cstr);
} else {
m_data = new char[1]; //[1]爲了和上面統一,析構時方便
*m_data = '\0';
}
}
C/C++語言中,字符串以'\0'表示一個字符串的結束
inline String::~String()
{
delete [] m_data;
}
如果類裏面有指針,動態分配內存,一定要再析構函數中釋放內存,避免內存泄漏
拷貝構造
String s1("hello");
String s2(s1);
|| 完全相同,都是調用的拷貝構造
String s2 = s1;
inline
String::String(const String &str)
{
m_data = new char[strlen(cstr)+1];
strcpy(m_data, cstr);
}
重載等號運算符
1. 自我檢查,如果是自己,直接返回
2. 刪除當前的內容
3. 獲取右值內容
4. 返回自身引用,可以連等
inline
String& String::operator=(const String& str)
{
if(this == &str)
{ return *this;}
delete [] m_data;
m_data = new char[strlen(str)+1];
strcpy(m_data, str.m_data);
return *this;
}
2. 在堆中創建對象
new : 先分配內存,在調用構造函數
當new一個對象的時候,實際上是 先分配內存,在調用類的構造函數
Complex *pc = new Complex(1,2);
void mem = operator new (sizeof(Complex)); --> 內部使用malloc分配內存
pc = static_cast<Complex*>(mem);
pc->Complex::Complex(1,2); ---> Complex::Complex(pc, 1, 2)
delete:先調用析構函數,再釋放內存
delete pc;
Complex::~Complex(pc);
operator delete(pc) ---> free(pc) 使用free釋放內存
在實際應用中,建議遵循誰創建,誰釋放的原則,儘量避免內存泄漏
3. 擴展
使用static 的單例
class A
{
public:
static A &getInstance()
{
static A a;
return a;
}
static int num;
private:
A()
{}
};
int A::num = 10; //類的靜態成員初始化方法
int main(int argc, char *argv[])
{
A::getInstance(); //通過類名來調用
return 0;
}
類模板
template <typename T>
class A
{
public:
A(T a)
:m_a(a)
{}
T m_a;
};
//調用
A<int> a; //類模板調用需要指明模板類型
函數模板
template <class T>
const T& min(const T&a, const T&b)
{
return b < a ? b:a;
}
//調用
class A
{
...
};
A a,b;
min(a,b) ===> 函數模板調用不需要指明參數對象,編譯器會對實參進行推導,然後確定類型
namespace 命名空間
#include <iostream>
using namespace std; //打開標準庫 的命名空間
//自定義命名空間
namespace mySpace
{
int a;
}
int main(int argc, char*argv[])
{
mySpace::a = 10;
return 0;
}
operator type() const;
參考鏈接: [C/C++]_[操作符重載operator type()和operator()的區別]
#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
class Total
{
public:
Total(float sum,float discount)
{
sum_ = sum;
discount_ = discount;
}
~Total(){}
operator std::string()
{
cout<<"operator std::string() "<<endl;
char str[128];
sprintf(str,"%f",sum_* discount_);
return std::string(str);
}
operator int()
{
cout<<"operator int()"<<endl;
return sum_* discount_;
}
operator float()
{
cout<<"operator float()"<<endl;
return sum_* discount_;
}
float operator()()
{
cout<<"float operator()() "<<endl;
return sum_* discount_;
}
float sum_;
float discount_;
};
ostream& operator<< (ostream &out, const Total & ths)
{
cout << "--------------------ostream& << (ostream &out, const Total & ths)"<<endl;
return out;
}
int main(int argc, char const *argv[])
{
Total to(89, 0.8);
cout << to << endl;
cout << to() << endl;
cout << (std::string)to << endl;
cout << (float)to << endl;
cout << (int)to << endl;
return 0;
}
輸出
--------------------ostream& << (ostream &out, const Total & ths)
float operator()()
71.2
operator std::string()
71.200001
operator float()
71.2
operator int()
71
需要注意的是如果沒有重載<<,並且Total裏面有operator float(),operator int()編譯器就會報錯,有二義性,因爲不確定cout << to << endl 是把Total轉成int,還是float,然後進行輸出,如果只有一個,就不會有問題
explicit complex():initialization list {}
關鍵字explicit,可以阻止不應該允許的經過轉換構造函數進行的隱式轉換的發生。聲明爲explicit的構造函數不能在隱式轉換中使用。
class Test1
{
public:
Test1(int n) { num = n; } //普通構造函數
private:
int num;
};
class Test2
{
public:
explicit Test2(int n) { num = n; } //explicit(顯式)構造函數
private:
int num;
};
int main(int argc, char *argv[])
{
Test1 t1 = 12; //隱式調用其構造函數, 成功
//Test2 t2 = 12; //編譯錯誤,不能隱式調用其構造函數
Test2 t3(12); //顯示調用成功
return 0;
}
function-like object
參考鏈接:std::function from cppreference.com
以下代碼需要編譯器C++11支持
#include <functional>
#include <iostream>
struct Foo {
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_+i << '\n'; }
int num_;
};
void print_num(int i)
{
std::cout << i << '\n';
}
struct PrintNum {
void operator()(int i) const
{
std::cout << i << '\n';
}
};
int main()
{
// store a free function
std::function<void(int)> f_display = print_num;
f_display(-9);
// store a lambda
std::function<void()> f_display_42 = []() { print_num(42); };
f_display_42();
// store the result of a call to std::bind
std::function<void()> f_display_31337 = std::bind(print_num, 31337);
f_display_31337();
// store a call to a member function
std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
const Foo foo(314159);
f_add_display(foo, 1);
// store a call to a data member accessor
std::function<int(Foo const&)> f_num = &Foo::num_;
std::cout << "num_: " << f_num(foo) << '\n';
// store a call to a member function and object
using std::placeholders::_1;
std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );
f_add_display2(2);
// store a call to a member function and object ptr
std::function<void(int)> f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );
f_add_display3(3);
// store a call to a function object
std::function<void(int)> f_display_obj = PrintNum();
f_display_obj(18);
}
//輸出
-9
42
31337
314160
num_: 314159
314161
314162
18
4. 作業知識點
虛析構
爲了當用一個基類的指針刪除一個派生類的對象時,派生類的析構函數會被調用。
class Shape
{
public:
Shape(int no = 0)
:m_no(no)
{ }
virtual ~Shape(){}
virtual int getArea()=0;
void setNo(int no) { m_no = no; }
int getNo() {return m_no;}
protected:
int m_no;
};
class Rect:public Shape
{
public:
Rect():m_p(0)
{}
~Rect(){}
private
char *m_p;
}
int main(int argc, char *argv[])
{
Shape *p = new Rect;
delete p;
return 0;
}
上例代碼,如果Shape的析構函數不是virtual的話,delete的時候就不會調用Rect的析構函數,可能會造成Rect::m_p的內存泄漏
還可以參考這個例子:析構函數什麼情況下要定義爲虛函數?