C++基础
文章目录
- C++命名空间
- 定义命名空间
- using指令
- 不连续的命名空间
- 嵌套的命名空间
- 面向对象
- 类
- 类成员的访问权限及类的封装
- 对象
- 类成员函数
- 类访问修饰符
- 构造函数和析构函数
- 类的构造函数
- 带参数的构造函数
- 使用初始化列表来初始化字段
- 类的析构函数
- 拷贝构造函数
- 友元函数
- 内联函数
- this指针
- 指向类的指针
- 类的静态成员
- 静态成员函数
- 继承和派生
- 访问控制和继承
- 继承类型
- 多继承
- 重载运算符和重载函数
- 函数重载
- 运算符重载
- 可重载运算符/不可重载运算符
- 动态内存
- new和delete运算符
- 数组的动态内存分配
- 对象的动态内存分配
C++命名空间
在 C++ 应用程序中,可能会写一个名为 xyz() 的函数,在另一个可用的库中也存在一个相同的函数 xyz()。这样,编译器就无法判断您所使用的是哪一个 xyz() 函数。
因此,引入了命名空间这个概念,专门用于解决上面的问题,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。
我们举一个计算机系统中的例子,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。
定义命名空间
命名空间的定义使用关键字 namespace,后跟命名空间的名称,如下所示:
为了调用带有命名空间的函数或变量,需要在前面加上命名空间的名称,如下所示:
#include <iostream>
using namespace std;// 第一个命名空间
namespace first_space{void func(){cout << "Inside first_space" << endl;}
}
// 第二个命名空间
namespace second_space{void func(){cout << "Inside second_space" << endl;}
}
int main ()
{// 调用第一个命名空间中的函数first_space::func();// 调用第二个命名空间中的函数second_space::func(); return 0;
}
using指令
可以使用 using namespace 指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。
#include <iostream>
using namespace std;// 第一个命名空间
namespace first_space{void func(){cout << "Inside first_space" << endl;}
}
// 第二个命名空间
namespace second_space{void func(){cout << "Inside second_space" << endl;}
}
using namespace first_space;
int main ()
{// 调用第一个命名空间中的函数func();return 0;
}
using 指令也可以用来指定命名空间中的特定项目。例如,如果只打算使用 std 命名空间中的 cout 部分,您可以使用如下的语句:
随后的代码中,在使用 cout 时就可以不用加上命名空间名称作为前缀,但是 std 命名空间中的其他项目仍然需要加上命名空间名称作为前缀。
#include <iostream>
using std::cout;int main ()
{cout << "std::endl is used with std!" << std::endl;return 0;
}
不连续的命名空间
命名空间可以定义在几个不同的部分中,因此命名空间是由几个单独定义的部分组成的。一个命名空间的各个组成部分可以分散在多个文件中。
所以,如果命名空间中的某个组成部分需要请求定义在另一个文件中的名称,则仍然需要声明该名称。下面的命名空间定义可以是定义一个新的命名空间,也可以是为已有的命名空间增加新的元素:
嵌套的命名空间
命名空间可以嵌套,您可以在一个命名空间中定义另一个命名空间,如下所示:
您可以通过使用 ::
运算符来访问嵌套的命名空间中的成员:
在上面的语句中,如果使用的是 namespace_name1,那么在该范围内 namespace_name2 中的元素也是可用的,如下所示:
#include <iostream>
using namespace std;// 第一个命名空间
namespace first_space{void func(){cout << "Inside first_space" << endl;}// 第二个命名空间namespace second_space{void func(){cout << "Inside second_space" << endl;}}
}
using namespace first_space::second_space;
int main ()
{// 调用第二个命名空间中的函数func();return 0;
}
面向对象
类
类也是一种数据类型。
类的声明:
成员函数的定义:类内,类外,类外内联函数
内联函数的代码会直接嵌入到主调函数中,可以节省调用时间,如果成员函数在类内定义,自动为内联函数。
类成员的访问权限及类的封装
- 和Java、C#不同的是,C++中public、private、protected只能修饰类的成员,不能修饰类,C++中的类没有共有私有之分
- 类内部没有访问权限的限制,都可以互相访问。*
- 在C++中用class定义的类中,其成员的默认存取权限是private。
对象
注:不可以在定义类的同时对其数据成员进行初始化,因为类不是一个实体,不合法但是能编译运行。
对象成员的引用:对象名.数据成员名
或者 对象名.成员函数名(参数列表)
私有成员和受保护成员不能直接使用成员访问运算符(.)来访问。
::
范围解析运算符。
类成员函数
类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员。
成员函数可以定义在类内部,或者在**类外部单独使用范围解析运算符:: **来定义。在类中定义的,即使没有把函数声明为内联(inline)的,也可以直接定义。
类内定义:
类外定义:
类外定义使用范围运算符::
,调用成员函数是使用(.
)。
类访问修饰符
public、private、protected 称为访问修饰符,用来防止函数直接访问类的内部成员,通过在类主题内部对各个区域进行标记来指定访问限制。默认private。
实际操作中,我们会在私有域定义数据,公有域定义相关函数,以便在累的外部也可以调用这些函数。
protected成员变量或函数与私有成员十分类似,不同的是,protected成员在派生类中是可访问的。
继承的特点:
private成员只能被本类成员和友元访问,不能被派生类访问。
构造函数和析构函数
类的构造是一种特殊的函数,在每次创建一个新的对象时使用。类的析构函数也是一种特殊函数,在删除所创建的对象时调用。
类的构造函数
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
#include <iostream>using namespace std;class Line
{public:void setLength( double len );double getLength( void );Line(); // 这是构造函数private:double length;
};// 成员函数定义
Line::Line(void)//定义构造函数
{cout << "Object is being created" << endl;
}void Line::setLength( double len )
{length = len;
}double Line::getLength( void )
{return length;
}
// 程序的主函数
int main( )
{Line line;// 设置长度line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl;return 0;
}
带参数的构造函数
默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值。
#include <iostream>using namespace std;class Line
{public:void setLength( double len );double getLength( void );Line(double len); // 这是构造函数private:double length;
};// 成员函数定义,包括构造函数
Line::Line( double len)
{cout << "Object is being created, length = " << len << endl;length = len;
}void Line::setLength( double len )
{length = len;
}double Line::getLength( void )
{return length;
}
// 程序的主函数
int main( )
{Line line(10.0);// 获取默认设置的长度cout << "Length of line : " << line.getLength() <<endl;// 再次设置长度line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl;return 0;
}
使用初始化列表来初始化字段
使用初始化列表来初始化字段:
Line::Line( double len): length(len)
{cout << "Object is being created, length = " << len << endl;
}
等同于如下语法:
Line::Line( double len)
{length = len;cout << "Object is being created, length = " << len << endl;
}
假设有一个类 C,具有多个字段 X、Y、Z 等需要进行初始化,同理地,您可以使用上面的语法,只需要在不同的字段使用逗号进行分隔。
C::C( double a, double b, double c): X(a), Y(b), Z(c)
{....
}
类的析构函数
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
#include <iostream>using namespace std;class Line
{public:void setLength( double len );double getLength( void );Line(); // 这是构造函数声明~Line(); // 这是析构函数声明private:double length;
};// 成员函数定义,包括构造函数
Line::Line(void)
{cout << "Object is being created" << endl;
}
Line::~Line(void)
{cout << "Object is being deleted" << endl;
}void Line::setLength( double len )
{length = len;
}double Line::getLength( void )
{return length;
}
// 程序的主函数
int main( )
{Line line;// 设置长度line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl;return 0;
}
拷贝构造函数
友元函数
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend,如下所示:
class Box
{double width;
public:double length;friend void printWidth( Box box );void setWidth( double wid );
};
声明类 ClassTwo 的所有成员函数作为类 ClassOne 的友元,需要在类 ClassOne 的定义中放置如下声明:
friend class ClassTwo;
#include <iostream>using namespace std;class Box
{double width;
public:friend void printWidth( Box box );void setWidth( double wid );
};// 成员函数定义
void Box::setWidth( double wid )
{width = wid;
}// 请注意:printWidth() 不是任何类的成员函数
void printWidth( Box box )
{/* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */cout << "Width of box : " << box.width <<endl;
}// 程序的主函数
int main( )
{Box box;// 使用成员函数设置宽度box.setWidth(10.0);// 使用友元函数输出宽度printWidth( box );return 0;
}
内联函数
C++ 内联函数是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。
如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字 inline,在调用函数之前需要对函数进行定义。如果已定义的函数多于一行,编译器会忽略 inline 限定符。
在类定义中的定义的函数都是内联函数,即使没有使用 inline 说明符。
#include <iostream>using namespace std;inline int Max(int x, int y)
{return (x > y)? x : y;
}// 程序的主函数
int main( )
{cout << "Max (20,10): " << Max(20,10) << endl;cout << "Max (0,200): " << Max(0,200) << endl;cout << "Max (100,1010): " << Max(100,1010) << endl;return 0;
}
this指针
在 C++ 中,每一个对象都能通过 this
指针来访问自己的地址。this
指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。
友元函数没有this
指针,因为友元不是类的成员。只有成员函数才有 this 指针。
#include <iostream>using namespace std;class Box
{public:// 构造函数定义Box(double l=2.0, double b=2.0, double h=2.0){cout <<"Constructor called." << endl;length = l;breadth = b;height = h;}double Volume(){return length * breadth * height;}int compare(Box box){return this->Volume() > box.Volume();}private:double length; // Length of a boxdouble breadth; // Breadth of a boxdouble height; // Height of a box
};int main(void)
{Box Box1(3.3, 1.2, 1.5); // Declare box1Box Box2(8.5, 6.0, 2.0); // Declare box2if(Box1.compare(Box2)){cout << "Box2 is smaller than Box1" <<endl;}else{cout << "Box2 is equal to or larger than Box1" <<endl;}return 0;
}
指向类的指针
一个指向 C++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->,就像访问指向结构的指针一样。与所有的指针一样,您必须在使用指针之前,对指针进行初始化。
#include <iostream>using namespace std;class Box
{public:// 构造函数定义Box(double l=2.0, double b=2.0, double h=2.0){cout <<"Constructor called." << endl;length = l;breadth = b;height = h;}double Volume(){return length * breadth * height;}private:double length; // Length of a boxdouble breadth; // Breadth of a boxdouble height; // Height of a box
};int main(void)
{Box Box1(3.3, 1.2, 1.5); // Declare box1Box Box2(8.5, 6.0, 2.0); // Declare box2Box *ptrBox; // Declare pointer to a class.// 保存第一个对象的地址ptrBox = &Box1;// 现在尝试使用成员访问运算符来访问成员cout << "Volume of Box1: " << ptrBox->Volume() << endl;// 保存第二个对象的地址ptrBox = &Box2;// 现在尝试使用成员访问运算符来访问成员cout << "Volume of Box2: " << ptrBox->Volume() << endl;return 0;
}
类的静态成员
我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。
静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化,
#include <iostream>using namespace std;class Box
{public:static int objectCount;// 构造函数定义Box(double l=2.0, double b=2.0, double h=2.0){cout <<"Constructor called." << endl;length = l;breadth = b;height = h;// 每次创建对象时增加 1objectCount++;}double Volume(){return length * breadth * height;}private:double length; // 长度double breadth; // 宽度double height; // 高度
};// 初始化类 Box 的静态成员
int Box::objectCount = 0;int main(void)
{Box Box1(3.3, 1.2, 1.5); // 声明 box1Box Box2(8.5, 6.0, 2.0); // 声明 box2// 输出对象的总数cout << "Total objects: " << Box::objectCount << endl;return 0;
}
静态成员函数
如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。
静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。
静态成员函数有一个类范围,他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。
静态成员函数与普通成员函数的区别:
- 静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
- 普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针。
#include <iostream>using namespace std;class Box
{public:static int objectCount;// 构造函数定义Box(double l=2.0, double b=2.0, double h=2.0){cout <<"Constructor called." << endl;length = l;breadth = b;height = h;// 每次创建对象时增加 1objectCount++;}double Volume(){return length * breadth * height;}static int getCount(){return objectCount;}private:double length; // 长度double breadth; // 宽度double height; // 高度
};// 初始化类 Box 的静态成员
int Box::objectCount = 0;int main(void)
{// 在创建对象之前输出对象的总数cout << "Inital Stage Count: " << Box::getCount() << endl;Box Box1(3.3, 1.2, 1.5); // 声明 box1Box Box2(8.5, 6.0, 2.0); // 声明 box2// 在创建对象之后输出对象的总数cout << "Final Stage Count: " << Box::getCount() << endl;return 0;
}
继承和派生
一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:
class derived-class: access-specifier base-class
访问修饰符 access-specifier 是 public、protected 或 private 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。
#include <iostream>using namespace std;// 基类
class Shape
{public:void setWidth(int w){width = w;}void setHeight(int h){height = h;}protected:int width;int height;
};// 派生类
class Rectangle: public Shape
{public:int getArea(){ return (width * height); }
};int main(void)
{Rectangle Rect;Rect.setWidth(5);#调用基类的函数Rect.setHeight(7);// 输出对象的面积cout << "Total area: " << Rect.getArea() << endl;# 派生类的函数return 0;
}
访问控制和继承
派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。
一个派生类继承了所有的基类方法,但下列情况除外:
- 基类的构造函数、析构函数和拷贝构造函数。
- 基类的重载运算符。
- 基类的友元函数。
继承类型
无论哪种继承方式:
- private成员只能被本类成员(类内)和友元访问,不能被派生类访问;
- protected成员可以被派生类访问。
publie继承:
#include<iostream>
#include<assert.h>
using namespace std;class A{
public:int a;A(){a1 = 1;a2 = 2;a3 = 3;a = 4;}void fun(){cout << a << endl; //正确cout << a1 << endl; //正确cout << a2 << endl; //正确cout << a3 << endl; //正确}
public:int a1;
protected:int a2;
private:int a3;
};
class B : public A{
public:int a;B(int i){A();a = i;}void fun(){cout << a << endl; //正确,public成员cout << a1 << endl; //正确,基类的public成员,在派生类中仍是public成员。cout << a2 << endl; //正确,基类的protected成员,在派生类中仍是protected可以被派生类访问。cout << a3 << endl; //错误,基类的private成员不能被派生类访问。}
};
int main(){B b(10);cout << b.a << endl;cout << b.a1 << endl; //正确cout << b.a2 << endl; //错误,类外不能访问protected成员cout << b.a3 << endl; //错误,类外不能访问private成员system("pause");return 0;
}
protected继承:
#include<iostream>
#include<assert.h>
using namespace std;
class A{
public:int a;A(){a1 = 1;a2 = 2;a3 = 3;a = 4;}void fun(){cout << a << endl; //正确cout << a1 << endl; //正确cout << a2 << endl; //正确cout << a3 << endl; //正确}
public:int a1;
protected:int a2;
private:int a3;
};
class B : protected A{
public:int a;B(int i){A();a = i;}void fun(){cout << a << endl; //正确,public成员。cout << a1 << endl; //正确,基类的public成员,在派生类中变成了protected,可以被派生类访问。cout << a2 << endl; //正确,基类的protected成员,在派生类中还是protected,可以被派生类访问。cout << a3 << endl; //错误,基类的private成员不能被派生类访问。}
};
int main(){B b(10);cout << b.a << endl; //正确。public成员cout << b.a1 << endl; //错误,protected成员不能在类外访问。cout << b.a2 << endl; //错误,protected成员不能在类外访问。cout << b.a3 << endl; //错误,private成员不能在类外访问。system("pause");return 0;
}
private继承:
#include<iostream>
#include<assert.h>
using namespace std;
class A{
public:int a;A(){a1 = 1;a2 = 2;a3 = 3;a = 4;}void fun(){cout << a << endl; //正确cout << a1 << endl; //正确cout << a2 << endl; //正确cout << a3 << endl; //正确}
public:int a1;
protected:int a2;
private:int a3;
};
class B : private A{
public:int a;B(int i){A();a = i;}void fun(){cout << a << endl; //正确,public成员。cout << a1 << endl; //正确,基类public成员,在派生类中变成了private,可以被派生类访问。cout << a2 << endl; //正确,基类的protected成员,在派生类中变成了private,可以被派生类访问。cout << a3 << endl; //错误,基类的private成员不能被派生类访问。}
};
int main(){B b(10);cout << b.a << endl; //正确。public成员cout << b.a1 << endl; //错误,private成员不能在类外访问。cout << b.a2 << endl; //错误, private成员不能在类外访问。cout << b.a3 << endl; //错误,private成员不能在类外访问。system("pause");return 0;
}
多继承
多继承即一个子类可以有多个父类,它继承了多个父类的特性。
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
<派生类类体>
};
其中,访问修饰符继承方式是 public、protected 或 private 其中的一个,用来修饰每个基类,各个基类之间用逗号分隔,如上所示。
#include <iostream>using namespace std;// 基类 Shape
class Shape
{public:void setWidth(int w){width = w;}void setHeight(int h){height = h;}protected:int width;int height;
};// 基类 PaintCost
class PaintCost
{public:int getCost(int area){return area * 70;}
};// 派生类
class Rectangle: public Shape, public PaintCost
{public:int getArea(){ return (width * height); }
};int main(void)
{Rectangle Rect;int area;Rect.setWidth(5);Rect.setHeight(7);area = Rect.getArea();// 输出对象的面积cout << "Total area: " << Rect.getArea() << endl;// 输出总花费cout << "Total paint cost: $" << Rect.getCost(area) << endl;return 0;
}
重载运算符和重载函数
C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。
重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。
当您调用一个重载函数或重载运算符时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策。
函数重载
在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,这就是函数重载。不能仅通过返回类型的不同来重载函数。
#include <iostream>
using namespace std;class printData
{public:void print(int i) {cout << "整数为: " << i << endl;}void print(double f) {cout << "浮点数为: " << f << endl;}void print(char c[]) {cout << "字符串为: " << c << endl;}
};int main(void)
{printData pd;// 输出整数pd.print(5);// 输出浮点数pd.print(500.263);// 输出字符串char c[] = "Hello C++";pd.print(c);return 0;
}
运算符重载
可以重定义或重载大部分 C++ 内置的运算符。这样,您就能使用自定义类型的运算符。
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
Box operator+(const Box&);
声明加法运算符用于把两个 Box 对象相加,返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数。
Box operator+(const Box&, const Box&);
下面实例使用成员函数演示了运算符重载的概念,例子中,对象作为参数进行传递,对象的属性使用this运算符进行访问。
#include <iostream>
using namespace std;class Box
{public:double getVolume(void){return length * breadth * height;}void setLength( double len ){length = len;}void setBreadth( double bre ){breadth = bre;}void setHeight( double hei ){height = hei;}// 重载 + 运算符,用于把两个 Box 对象相加Box operator+(const Box& b){Box box;box.length = this->length + b.length;box.breadth = this->breadth + b.breadth;box.height = this->height + b.height;return box;}private:double length; // 长度double breadth; // 宽度double height; // 高度
};
// 程序的主函数
int main( )
{Box Box1; // 声明 Box1,类型为 BoxBox Box2; // 声明 Box2,类型为 BoxBox Box3; // 声明 Box3,类型为 Boxdouble volume = 0.0; // 把体积存储在该变量中// Box1 详述Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0);// Box2 详述Box2.setLength(12.0); Box2.setBreadth(13.0); Box2.setHeight(10.0);// Box1 的体积volume = Box1.getVolume();cout << "Volume of Box1 : " << volume <<endl;// Box2 的体积volume = Box2.getVolume();cout << "Volume of Box2 : " << volume <<endl;// 把两个对象相加,得到 Box3Box3 = Box1 + Box2;// Box3 的体积volume = Box3.getVolume();cout << "Volume of Box3 : " << volume <<endl;return 0;
}
可重载运算符/不可重载运算符
动态内存
了解动态内存在 C++ 中是如何工作的是成为一名合格的 C++ 程序员必不可少的。C++ 程序中的内存分为两个部分:
- 栈:在函数内部声明的所有变量都将占用栈内存。
- 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。
很多时候,您无法提前预知需要多少内存来存储某个定义变量中的特定信息,所需内存的大小需要在运行时才能确定。
在 C++ 中,您可以使用特殊的运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。这种运算符即 new
运算符。
如果您不再需要动态分配的内存空间,可以使用 delete
运算符,删除之前由 new 运算符分配的内存。
new和delete运算符
使用new运算符来为任意的数据类型动态分配内存的通用语法:
new data-type
data-type可以是包括数组在内的任意内置的数据类型,也可以是包括类或结构在内的用户自定义的任何数据类型。
例如,定义一个指向 double 类型的指针,然后请求内存,该内存在执行时被分配。
double* pvalue = NULL; // 初始化为 null 的指针
pvalue = new double; // 为变量请求内存
如果自由存储区已被用完,可能无法成功分配内存。所以建议检查 new 运算符是否返回 NULL 指针,并采取以下适当的操作:
double* pvalue = NULL;
if( !(pvalue = new double ))
{cout << "Error: out of memory." <<endl;exit(1);
}
malloc() 函数在 C 语言中就出现了,在 C++ 中仍然存在,但建议尽量不要使用 malloc() 函数。new 与 malloc() 函数相比,其主要的优点是,new 不只是分配了内存,它还创建了对象。
在任何时候,当您觉得某个已经动态分配内存的变量不再需要使用时,您可以使用 delete 操作符释放它所占用的内存,如下所示:
delete pvalue; //释放pvalue所指向的内存
如下实例展示了如何使用new和delete运算符
#include <iostream>
using namespace std;int main ()
{double* pvalue = NULL; // 初始化为 null 的指针pvalue = new double; // 为变量请求内存*pvalue = 29494.99; // 在分配的地址存储值cout << "Value of pvalue : " << *pvalue << endl;delete pvalue; // 释放内存return 0;
}
数组的动态内存分配
假设我们要为一个字符数组(一个有 20 个字符的字符串)分配内存,我们可以使用上面实例中的语法来为数组动态地分配内存,如下所示:
char* pvalue = NULL; // 初始化为 null 的指针
pvalue = new char[20]; // 为变量请求内存
删除刚才创建的数组
delete [] pvalue; // 删除 pvalue 所指向的数组
可以为多多维数组分配内存:
一维数组:
// 动态分配,数组长度为 m
int *array=new int [m];//释放内存
delete [] array;
二维数组:
int **array;
// 假定数组第一维长度为 m, 第二维长度为 n
// 动态分配空间
array = new int *[m];
for( int i=0; i<m; i++ )
{array[i] = new int [n];
}
//释放
for( int i=0; i<m; i++ )
{delete [] array[i];
}
delete [] array;
实例:
#include <iostream>
using namespace std;int main()
{int **p; int i,j; //p[4][8] //开始分配4行8列的二维数据 p = new int *[4];for(i=0;i<4;i++){p[i]=new int [8];}for(i=0; i<4; i++){for(j=0; j<8; j++){p[i][j] = j*i;}} //打印数据 for(i=0; i<4; i++){for(j=0; j<8; j++) { if(j==0) cout<<endl; cout<<p[i][j]<<"\t"; }} //开始释放申请的堆 for(i=0; i<4; i++){delete [] p[i]; }delete [] p; return 0;
}
对象的动态内存分配
对象与简单的数据类型没有什么不同。
#include <iostream>
using namespace std;class Box
{public:Box() { cout << "调用构造函数!" <<endl; }~Box() { cout << "调用析构函数!" <<endl; }
};int main( )
{Box* myBoxArray = new Box[4];delete [] myBoxArray; // 删除数组return 0;
}
如果要为一个包含四个 Box 对象的数组分配内存,构造函数将被调用 4 次,同样地,当删除这些对象时,析构函数也将被调用相同的次数(4次)。
参考链接:菜鸟教程C++
相关文章:

C++基础
文章目录 C命名空间定义命名空间using指令不连续的命名空间嵌套的命名空间 面向对象类类成员的访问权限及类的封装对象类成员函数类访问修饰符构造函数和析构函数类的构造函数带参数的构造函数使用初始化列表来初始化字段类的析构函数拷贝构造函数 友元函数内联函数this指针指向…...
软件测试全流程
软件测试全流程 一、制定测试策略二、制定测试方案三、编辑测试用例四、执行测试用例五、输出问题单六、回归测试七、测试文件归档 一、制定测试策略 1、测试目的测试范围 2、用什么测试方法工具(例如功能测试用黑盒测试) 3、测试优先级(功能…...

【软件测试】支付模块测试攻略,这些测试方法和注意事项你掌握了么?
对于大部分人而言,支付模块或许是日常生活中最为关注和使用的功能之一,因此,对于支付模块的质量控制也显得尤为重要。 但考虑到支付涉及到金钱流转等敏感信息,一旦出现问题可能带来非常严重后果。因此,在支付模块测试…...

刷完这个笔记,17K不能再少了....
大家好,最近有不少小伙伴在后台留言,得准备面试了,又不知道从何下手!为了帮大家节约时间,特意准备了一份面试相关的资料,内容非常的全面,真的可以好好补一补,希望大家在都能拿到理想…...
知识变现创业指南-《知识变现秘籍》
《知识变现秘籍》 知识变现创业者指南 读完将改变你的认知 开阔你的知识变现思路 系统掌握知识变现的要点 知识付费创业方法 帮你利用知识赚到你弟一桶金 如果你有一技之长,想变现 如果你有一身才华,想变现 如果你在某个领域有绝活 如果你是&am…...

springboot+java博物馆文物管理系统
用户前台进入系统可以进行首页、文物信息、论坛交流、文物资讯、留言反馈、我的、跳转到后台等springboot是基于spring的快速开发框架, 相比于原生的spring而言, 它通过大量的java config来避免了大量的xml文件, 只需要简单的生成器便能生成一个可以运行的javaweb项目, 是目前最…...
Ansible 自动化运维工具(二)——Ansible 的脚本(playbook 剧本)
Ansible 自动化运维工具—Ansible 的脚本playbook 剧本 playbooks 概述以及实例操作playbooks 的组成操作示例一:编写yaml文件也就是playbook修改配置文件并放入/opt/目录下运行playbook 操作实例二:定义、引用变量操作示例三:指定远程主机su…...

阿里云镜像服务下载并安装Go环境
【阿里云镜像】下载并安装Go环境 一、参考链接 阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 (aliyun.com) golang镜像-golang下载地址-golang安装教程-阿里巴巴开源镜像站 (aliyun.com) GO语言安装以及国内镜像 - DbWong_0918 - 博客园 (cnblogs.com) 二、Go介绍 Gol…...
工作线程快速优雅退出方式探讨
本文我们不用定时器。定时器会阻塞消息循环。先看需求: 我们先看第一种,有一个任务,要求每1秒钟执行一次,最常见的写法如下两种 bool bExitThread false; DWORD WorkThread1(LPVOID param) {while (false bExitThread){//// to…...

甘特图控件DHTMLX Gantt教程:用PHP:Laravel实现Gantt(上)
DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表。可满足项目管理应用程序的大部分开发需求,具备完善的甘特图图表库,功能强大,价格便宜,提供丰富而灵活的JavaScript API接口,与各种服务器端技术&am…...
ffmpeg-命令大全03
ffplay/mpv 查看所有的解码器 ffplay -decoders >>decoders.txt ffplay无延时无缓存播放 ffplay -fflags nobuffer -i "%1" ffplay指定size播放 ffplay -x 480 -y 270 -i "%1" ffplay指定解码器 ffplay -vcodec hevc_cuvid "%1" […...
MATLAB中太赫兹时域光谱的最大似然参数估计
目录 一、引言 二、最大似然估计的基本原理 三、MATLAB中的最大似然估计实现 四、太赫兹时域光谱的最大似然参数估计 五、结论 六、参考文献 一、引言 太赫兹波(Terahertz wave)是电磁谱中介于微波与光波之间的一段频率范围,频率大约在…...

详解MySQL的并发控制
目录 1.概述 2.事务 2.1.什么是事务 2.2.事务的隔离级别 2.2.1.三种数据一致性问题 2.2.2.四种隔离级别 2.3.如何设置隔离级别 3.锁 3.1.锁与事务的关系 3.2.分类 3.3.表锁 3.3.1.概述 3.3.2.读锁 3.3.3.写锁 3.3.4.保护机制 3.4.行锁 3.4.1.概述 3.4.2.什么…...

Android Termux安装MySQL数据库 | 公网安全远程连接【cpolar内网穿透】
文章目录 前言1.安装MariaDB2.安装cpolar内网穿透工具3. 创建安全隧道映射mysql4. 公网远程连接5. 固定远程连接地址 前言 Android作为移动设备,尽管最初并非设计为服务器,但是随着技术的进步我们可以将Android配置为生产力工具,变成一个随身…...

SpringBoot的常见配置
SpringBoot基础配置 1. 配置文件格式问题导入1.1 修改服务器端口1.2 自动提示功能消失解决方案1.3 SpringBoot配置文件加载顺序 2. yaml问题导入2.1 yaml语法规则2.2 yaml数组数据2.3 yaml数据读取 3. 多环境开发配置问题导入3.1 多环境启动配置3.2 多环境启动命令格式3.3 多环…...

LabVIEWCompactRIO 开发指南25 实施LabVIEW FPGA代码的方法
LabVIEWCompactRIO 开发指南25 实施LabVIEW FPGA代码的方法 开始开发时,应在LabVIEW项目的FPGA目标下创建VI,以便使用LabVIEW FPGA选板进行编程,该选板是LabVIEW选板的子集,包括一些LabVIEW FPGA特定函数。 应该在仿真模式下开…...

如何利用Jmeter从0到1做一次完整的压测
压测,在很多项目中都有应用,是测试小伙伴必备的一项基本技能,刚好最近接手了一个小游戏的压测任务,一轮压测下来,颇有收获,赶紧记录下来,与大家分享一下,希望大家能少踩坑。 一、压测…...

蓝桥:前端开发笔面必刷题——Day1 数组(一)
文章目录 📋前言🎯数组中重复的数字📚题目内容✅解答 🎯两数之和📚题目内容✅解答 🎯替换空格📚题目内容✅解答 🎯二维数组中的查找📚题目内容✅解答 📝最后 …...
SQL 教程-入门基础篇
文章目录 SQL 简介SQL 语法SQL SELECT 语句SQL SELECT DISTINCT 语句SQL WHERE 子句SQL AND & OR 运算符SQL ORDER BY 关键字SQL INSERT INTO 语句SQL UPDATE 语句SQL DELETE 语句 SQL 简介 SQL(Structured Query Language)是一种用于管理和操作关系…...
循环自相关函数和谱相关密度(六)——信号的循环平稳性(循环自相关函数)原理及推导
在通信、遥测、雷达和声纳系统中,一些人工信号是一类特殊的非平稳信号,它们的非平稳性表现为周期平稳性。通信信号常用待传输信号对周期性信号的某个参数进行调制、如对正弦载波进行调幅、调频和调相,以及对周期性脉冲信号进行脉幅、脉宽和脉位调制,都会产生具有周期平稳性…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...