您的位置:首页 > 编程语言 > C语言/C++

[Boolan] C++第二周(创建一个带指针成员变量的类)[注意事项]

2017-01-21 21:09 555 查看

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的内存泄漏


还可以参考这个例子:析构函数什么情况下要定义为虚函数?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: