个人觉得学习编程最有效的方法是阅读专业的书籍,通过阅读专业书籍可以构建更加系统化的知识体系 一直以来都很想深入学习一下C ,将其作为自己的主力开发语言现在为了完成自己这一直以来的心愿,准备认真学习《C Primer Plus》 为了提高学习效率,在学习的过程中将通过发布学习笔记的方式,持续记录自己学习C 的过程,今天小编就来说说关于cprimerplus讲没讲排序算法:CPrimerPlus学习笔记之类和动态内存分配?下面更多详细答案一起来看看吧!
cprimerplus讲没讲排序算法:CPrimerPlus学习笔记之类和动态内存分配
前言个人觉得学习编程最有效的方法是阅读专业的书籍,通过阅读专业书籍可以构建更加系统化的知识体系。 一直以来都很想深入学习一下C ,将其作为自己的主力开发语言。现在为了完成自己这一直以来的心愿,准备认真学习《C Primer Plus》。 为了提高学习效率,在学习的过程中将通过发布学习笔记的方式,持续记录自己学习C 的过程。
一、动态内存和类不能在类声明中初始化静态成员变量,因为声明描述了如何分配内存,但并不分配内存。对于静态类成员,可以在类声明之外使用单独的语句来初始化,因为静态类成员是单独存储的,而不是对象的组成部分。初始化语句指出了类型,并使用了作用域运算符,但没有使用关键字static。
对于不能在类声明中初始化静态成员数据的一种例外情况,是静态数据成员为const整数类型或枚举类型。
删除对象可以释放对象本身占用的内存,但并不能自动释放属于对象成员的指针指向的内存。因此,必须使用析构函数。
将一个对象赋值给另一个对象,代码如下:
StringBad sailor = sports;
以上代码等效于下面的代码:
StringBad sailor = StringBad(sports);
对应的原型如下:
StringBad(const StringBad &);
C 自动提供了下面这些成员函数:
- 默认构造函数,如果没有定义构造函数;
- 默认析构函数,如果没有定义;
- 复制构造函数,如果没有定义;
- 赋值运算符,如果没有定义;
- 地址运算符,如果没有定义。
在新建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用。按值传递意味着创建原始变量的一个副本。 默认的复制构造函数逐个复制非静态成员(成员复制也称为浅复制),复制的是成员的值。
将已有的对象赋给另一个对象时,将使用重载的复制运算符。
二、改进后的新String类语句str = new char[1];与str = new char;两种方式分配的内存量相同,区别在与前者与类析构函数兼容,而后者不兼容。
C 提供一个新的关键字nullptr,用于表示空指针。
在C 中,两个中括号组成一个运算符——中括号运算符,可以使用方法operator[]()来重载该运算符。
可以将成员函数声明为静态的(函数声明必须包含关键字static,但如果函数定义是独立的,则其中不能包含关键字static),这样做有两个重要的后果:
- 不能通过对象调用静态成员函数;实际上,静态成员函数甚至不能使用this指针。如果静态成员函数是在公有部分声明的,可以使用类名和作用域解析运算符来调用它。
- 由于静态函数不与特定的对象相关联,因此只能使用静态数据成员。
至此,您知道使用new初始化对象的指针成员时必须特别小心。具体地说,应当这样做:
- 如果在构造函数中使用new来初始化指针成员,则应在析构函数中使用delete。
- new和delete必须相互兼容。new对应于delete,new[]对应于delete[]。
- 如果有多个构造函数,则必须以相同的方式使用new,要么都带中括号,要么都不带。
- 应定义一个复制构造函数,通过深度复制将一个对象初始化为另一个对象。
- 应当定义一个复制运算符,通过深度复制将一个对象复制给另一个对象。
当成员函数或独立的函数返回对象时,有几种返回方式可供选择。可以返回指向对象的引用、指向对象的const引用或const对象。 使用const引用的常见原因是旨在提高效率,如果函数返回(通过调用对象的方法或将对象作为参数)传递给它的对象,可以通过返回引用来提高效率。 两种常见的返回非const对象情形是,重载赋值运算符以及重载与cout一起使用的运算符。前者这样做旨在提高效率,而后者必须这样做。 如果被返回的对象是被调用函数中的局部变量,则不应按引用方式返回它,因为在被调用函数执行完毕时,局部对象将调用其析构函数。因此,当控制权回到调用函数时,引用指向的对象将不再存在。在这种情况下,应返回对象而不是引用。通常,被重载的算术运算符属于这一类。
五、使用指向对象的指针在下属情况下析构函数将被调用:
- 如果对象时动态变量,则当执行完定义该对象的程序块时,将调用该对象的析构函数。
- 如果对象时静态变量(外部、静态、静态外部或来自命名空间),则在程序结束时将调用对象的析构函数。
- 如果对象时用new创建的,则仅当您显式使用delete删除对象时,其析构函数才会被调用。
使用对象指针时,需要注意几点:
- 使用常规表示法来声明指向对象的指针:
String * glamour;
- 可以将指针初始化为指向已有的对象:
String * first = &sayings[0];
- 可以使用new来初始化指针,这将创建一个新的对象:
String * favorite = new String(sayings[choice]);
- 对类使用new将调用相应的类构造函数来初始化新创建的对象:
//调用默认构造函数
String * gleep = new String;
//调用String(const char *)构造函数
String * glop = new String("mymy");
//调用String(const String &)构造函数
String * favorite = new String(sayings[choice]);
- 可以使用->运算符通过指针访问类方法:
if(sayings[i].length() < shortest -> length())
- 可以对对象指针应用接触引用运算符(*)来获得对象:
if(sayings[i] < *first)
first = &sayings[i];
要重新定义<<运算符,以便将它和cout一起用来显示对象的内容,请定义下面的友元运算符函数:
ostream & operator<<(ostream & os, const c_name & obj)
{
os << ...;
return os;
}
其中c_name是类名。
2、转换函数要将单个值转换为类类型,需要创建原型如下所示的类构造函数:
c_name(type_name value);
其中c_name为类名,type_name是要转换的类型的名称。 要将类转换为其他类型,需要创建原型如下所示的类成员函数:
operator type_name();
虽然该函数没有声明返回类型,但应返回所需类型的值。 可以在声明构造函数时使用关键字explicit,以防止它被用于隐式转换。
3、其构造函数使用new的类注意以下几点:
- 对于指向的内存是由new分配的所有类成员,都应在类的析构函数中对其使用delete,该运算符将释放分配的内存。
- 如果析构函数通过对指针类成员使用delete来释放内存,则每个构造函数都应当使用new来初始化指针,或将它设置为空指针。
- 构造函数中要么使用new[],要么使用new,而不能混用。如果构造函数使用的是new[],则析构函数应使用delete[],如果构造函数使用的是new,则析构函数应使用delete。
- 应定义一个分配内存(而不是将指针指向已有内存)的复制构造函数。这样程序将能够将类对象初始化为另一个类对象。这种构造函数的原型通常如下:
className(const className &)
- 应定义一个重载赋值运算符的类成员函数,其函数定义如下(其中c_pointer是c_name的类成员,类型为指向type_name的指针)。下面使用new[]来初始化变量c_pointer:
c_name & c_name::operator=(const c_name & cn)
{
if(this == & cn)
return *this;
delete [] c_pointer;
c_pointer = new type_name[size];
...
return *this;
}
在类声明中声明的结构、类和枚举被称为是嵌套在类中,其作用域为整个类。
对于const数据成员,必须在执行到构造函数体之前,即创建对象时进行初始化。C 提供了一种特殊的语法来完成这种工作,它叫做成员初始化列表(member initializer list)。成员初始化列表由逗号分隔的初始化列表组成(前面带冒号)它位于参数列表的有括号之后、函数体左括号之前。例如:
Queue:Queue(int qs) : qsize(qs)//初始化qsize值为qs
{
front = rear = NULL;
items = 0;
}
只有构造函数可以使用这种初始化列表语法。对于const类成员和被声明为引用的类成员,必须使用这种语法。 数据成员被初始化的顺序与它们出现在类声明中的顺序相同,与初始化器中的排列顺序无关。
定义伪私有方法,可以避免本来将自动生成的默认方法定义,和这些方法是私有的不能广泛使用。
,