一、使用new初始化对象
通常,如果Class_name是类,value的类型为Type_name,则下面的语句:
Class_name *pclass = new Class_name(value);
将调用如下的构造函数:
Class_name(Type_name);
这里会有一些琐碎的转换,如:
Class_name(const Type_name &);
另外,如果不存在二义性,则将发生由原型匹配导致的转换(如int转为double)。
下面的初始化方式将调用默认构造函数:
Class_name *pclass = new Class_name;
二、随机数
插入一条随机数的使用:
// 使用srand() rand() time()
#include <ctime>
#include <cstdlib>
int a = 9;
srand(time(0));
int b = rand() % a;
三、再谈new与delete
根据下面使用,进行说明:
// 这不是new为要存储的字符串分配内存,而是为对象分配内存,也就是说为String类的成员变量分配内存。
String *str = new String("Hello World");
再看一个示例说明程序运行阶段的内存释放情况:
class Act
{
// 声明的变量与方法
...
};
Act nice; // 外部对象
int main()
{
Act *pt = new Act; // 动态对象
{
Act act; // 自动对象(局部变量)
...
} // 执行到定义的代码块末尾时,将自动调用对应act对象的析构函数
delete pt; // 对指针pt应用delete运算符时,将调用动态对象*pt的析构函数
...
} // 整个程序运行结束时,将调用静态对象nice的析构函数
在以下几种情况下析构函数将被调用:
(1)如果对象是动态变量,则当执行完定义该对象的程序块时,将调用该对象的析构函数。
(2)如果对象是静态变量(外部变量、静态变量、静态外部变量或来自名称空间),则在程序结束时将调用对象的析构函数。
(3)如果对象是用new创建的,则仅当显式使用delete删除对象时,该对象的析构函数才被调用。
四、指针和对象小结
使用对象指针时,需要注意以下几点:
(1)使用常规表示法来声明指向对象的指针:
String *ptr;
(2)可以将指针初始化为指向已有的对象:
String str = "Hello World";
String *ptr = &str;
(3)可以使用new来初始化指针,这将创建一个新对象:
String str = "Hello World";
String *ptr = new String(str);
(4)对类使用new将调用相应的类构造函数来初始化新创建的对象:
// 调用默认构造函数
String *ptr = new String;
// 调用String(const char *)构造函数
String *ptr = new String("Test");
// 调用复制构造函数
String *ptr = new String(str);
(5)可以使用->运算符通过指针访问类方法:
String *ptr = new String;
String *pst = new String;
if (ptr->length() < pst->length())
(6)可以对对象指针应用解除引用运算符(*)来获得对象:
if (str < *ptr)
ptr = &str;
五、再谈定位new运算符
定位new运算符可以在分配内存时指定分配的内存位置,根据以下代码实例进行分析说明:
const int BUF = 512;
class JustTesting
{
private:
std::string words;
int number;
public:
JustTesting(const std::string & s = "Just Testing", int n = 0);
~JustTesting();
void Show() const;
};
int main()
{
// 使用new运算符创建一个512字节的内存缓冲区
char* buffer = new char[BUF];
JustTesting *pc1, *pc2;
// 使用定位new运算符在内存缓冲区创建对象
pc1 = new (buffer) JustTesting;
// 使用new在堆上创建对象
pc2 = new JustTesting("Heap1", 20);
std::cout << "Memory block address:\n" << "buffer: "
<< (void*)buffer << " heap:" << pc2
<< std::endl;
std::cout << "Memory contents:\n";
std::cout << pc1 << ": ";
pc1->Show();
std::cout << pc2 << ": ";
pc2->Show();
JustTesting *pc3, *pc4;
pc3 = new (buffer) JustTesting("Bad Idea", 6);
pc4 = new JustTesting("Heap2", 10);
std::cout << "Memory contents:\n";
std::cout << pc3 << ": ";
pc3->Show();
std::cout << pc4 << ": ";
pc4->Show();
delete pc2;
delete pc4;
delete [] buffer;
}
上述代码示例存在的问题:
(1)使用定位new运算符创建第二个对象时,将使用一个新对象来覆盖用于第一个对象的内存单元。显然,如果类动态的为其成员分配内存,这将引发问题。
(2)将delete应用于pc2和pc4,将自动调用pc2和pc4指向的对象的析构函数,然而 ,将delete[]应用于buffer时,不会为使用定位new运算符创建的对象调用析构函数。原因在于delete可与常规new运算符配合使用,但不能与定位new运算符配合使用。
解决定位new运算符创建的对象的内存释放方案:
显式的为使用定位new运算符创建的对象调用析构函数,这是需要显式调用析构函数的情形之一。显式的调用析构函数时,必须指定要销毁的对象,由于有指向对象的指针,因此可以使用这些指针,使用如下示例:
pc3->~JustTesting();
pc1->~JustTesting();
delete[] buffer;
使用定位new运算符创建的对象显式调用析构函数需注意以下事项:
(1)对于使用定位new运算符创建的对象,应以与创建顺序相反的顺序进行删除(先创建后释放)。原因在于,晚创建的对象可能依赖于早创建的对象。
(2)仅当所有对象都被销毁后,才能释放用于存储这些对象的缓冲区。