本章主要讲解new运算符在各种情况下的使用注意事项。
c++使用new和delete运算符来动态控制内存。
一、关于静态成员
(1)静态成员属于类,不属于对象,即在多个对象中只有一个静态成员对象副本。
(2)静态数据成员在类中声明,在包含类方法的文件中初始化。但如果静态成员是const整型类型或者枚举类型,则可以在类声明中初始化。
(3)静态成员函数由于没有this指针,因此只能访问静态成员。对象要通过作用域限定符来调用静态成员方法。
在自定义类String要考虑浅拷贝与深拷贝的问题,下面对自定义类中默认方法做个分析,要对函数做深拷贝和浅拷贝的考虑。
二、关于自定义类默认生成的函数
默认构造函数、默认析构函数、拷贝构造函数、赋值运算符、地址运算符
(1)默认构造函数:
带参数的构造函数也可以是默认构造函数,只要所有的参数都有默认值。
在构造函数中使用new分配内存,必须在相应的析构函数中使用delete来释放内存。
(2)拷贝构造函数
拷贝函数调用的三种情况。
- 新建一个对象并初始化化同类现有对象时,会调用拷贝构造函数。
String s1(s2);
String s1 = s2;
String s1 = String(s2);
String *p2 = new String(s2);
- 当函数按值传递时,会调用拷贝函数
- 返回局部对象时,会调用拷贝构造函数。
由于按值传递都将调用拷贝构造函数,因此应该按照引用来传递,提高效率。
默认拷贝构造函数逐个复制数据成员,复制的是成员的值,这样就会造成浅拷贝。
浅拷贝的指的是有复制数据成员指针的值,但没有为分配空间,造成两个指针指向同一块内存,使得析构函数
对这块内存释放了两次,因此对拷贝构造函数进行重载。
(3)赋值运算符
String s1 = s2;
关于这句代码可能有两种使用步骤:
一是初始化,直接调用拷贝构造函数。二是先调用拷贝构造函数,然后使用赋值运算符。
在自定义类型String中,关于赋值运算符要实现深拷贝。
三、在构造函数中使用new的注意事项:
1- 如果构造函数使用new来初始化指针成员,则应该在析构函数中使用delete
2- new对于delete ,new [ ] 对应delete [ ]
3- 多个构造函数中必须使用相同的方式使用new,但可以在一个构造函数中使用new,在另一个构造函数中将指针成员置空(c++新的特性使用nullptr表示空指针)
四、有关返回对象的说明
(1)返回指向const对象的引用,(不会调用拷贝构造函数)旨在提高效率。
(2)返回指向非const对象的引用,这里主要考虑对流运算符的重载(<< 或者 >>).
(3)返回对象,不能返回局部的引用。
(4)返回const对象,防止在if判断中将==写成=,改变对象的值,保护数据。
五、析构函数调用的情况
(1)对象是动态,执行完定义该对象的程序是,将调用对象的析构函数。
(2)如果对象时静态(外部、静态(外部)或者来着名称空间),程序解释时,调用析构函数
(3)如果对象时new创建的,显式调用delete删除对象,将调用析构函数,否则该对象的析构函数不会被调用。
六、关于placement new 运算符
palacement new
在已有的内存上分配空间,不分配空间,只是构造函数的调用。
void * operator new(size_t size,void *p)
{
return p;
}
cahr buffer[1024]
Test *p2 = new(buffer) Test(200);//operator new(sizo_t ,void *p)
cout << p2->n_ <<endl;//placement new 不分配内存 + 构造函数。
自定义类String
String.h
#ifndef _String_H_
#define _String_H_
#include <iostream>
using namespace std;
class String
{
public:
String(const char *s);
String(const String &s);
String &operator=(const String &s);
String &operator=(const char *s);
String();
~String();
char & operator[](int i);
const char & operator[](int i) const;
friend bool operator<(const String &s1,const String &s2);
friend bool operator>(const String &s1,const String &s2);
friend bool operator==(const String &s1,const String &s2);
friend ostream &operator<<(ostream &out,const String &st);
friend istream &operator>>(istream &in,String &st);
static int HowMany();
private:
char *str;
int len;
static int num_strings;
static const int MAXSIZE = 80;
};
#endif //_STRING_H_
String.cpp
#include <iostream>
#include <cstring>
#include "StringBad.h"
using namespace std;
int String::num_strings = 0;
String::String(const char *s)
{
len = strlen(s);
str = new char[len + 1];
strcpy(str,s);
num_strings++;
// cout << num_strings << ":" << str << "\"object created " << endl;
}
String::String(const String &s)
{
num_strings++;
len = s.len;
str = new char[len + 1];
strcpy(str,s.str);
// cout <<num_strings << ": \"" << str << "\" object created" << endl;
}
String & String::operator=(const String & s)
{
// cout << "operator = " << endl;
if(this == &s)
{
return *this;
}
delete [] str;
len = s.len;
str = new char[len + 1];
strcpy(str,s.str);
return *this;
}
String & String::operator=(const char *s)
{
delete [] str;
len = strlen(s);
str = new char[len + 1];
strcpy(str,s);
return *this;
}
String::String()
{
len = 4;
str = new char[1];
str[0] = '\0';
num_strings++;
// cout << num_strings << ":" << str << "\"default object created" << endl;
}
String::~String()
{
// cout << "\"" << str << "\"object deleted, ";
--num_strings;
// cout << num_strings << endl;
delete [] str;
}
bool operator<(const String &s1,const String &s2)
{
return strcmp(s1.str,s2.str) < 0;
}
bool operator>(const String &s1,const String &s2)
{
return s2 < s1;
}
bool operator==(const String &s1,const String &s2)
{
return strcmp(s1.str,s2.str) == 0;
}
char & String::operator[](int i)
{
return str[i];
}
const char & String::operator[](int i) const
{
return str[i];
}
ostream & operator<<(ostream &out,const String &st)
{
out << st.str;
return out;
}
istream &operator>>(istream &in,String &st)
{
char temp[80];
in.get(temp,80);
if(in)
{
st = temp; // operator=(const char *);
}
while(in && in.get() != '\n')
continue;
return in;
}
int String::HowMany()
{
return num_strings;
}
main.cpp
#include <iostream>
#include "StringBad.h"
using namespace std;
void callme1(String &);
void callme2(String );
int main()
{
String name;
cin >> name;
cout << name << endl;
return 0;
}
void callme1(String &rsb )
{
cout << "callme1 " << endl;
}
void callme2(String rsb)
{
cout << "callem2" << endl;
}