logo

Crowdly

Browser

Add to Chrome

met2502f25-a.sg

Looking for met2502f25-a.sg test answers and solutions? Browse our comprehensive collection of verified answers for met2502f25-a.sg at distance3.sg.digipen.edu.

Get instant access to accurate answers and detailed explanations for your course questions. Our community-driven platform helps students succeed!

Just as in C, the type qualifier const is used in C++ to declare and initialize a read-only variable whose value cannot be changed during program execution. Therefore, the following code fragment will be flagged as a compile-time error:

int const value {10};

std::cout << "value: " << value << "\n";

value = 20; // error: assigning a new value to a const variable

std::cout << "value: " << value << "\n";

In both languages, the rule is that a const variable must be initialized during its definition. This makes sense since if a const variable's value cannot be changed during its lifetime, it can only be given a value when its lifetime begins. Therefore, the following code will be flagged as a compile-time error:

int const value; // error: const variable is not initialized during its definition

value = 20;

A constant expression is an expression whose value can be evaluated at compile-time and whose value cannot be changed during program execution. A literal value such as and is a constant expression. It may seem strange but a const variable need not be a constant expression. As an example:

void foo(int n) {

double pi = 3.14285714; // 3.14285714 is a constant expression

const int size = n; // not a constant expression

// other code here ...

}

literal is a constant expression while read-only variable size is not a constant expression since its value is only known at run-time.

Additionally in C++, a const variable initialized from a constant expression is also a constant expression. As an example:

void foo(int n) {

const double pi = 3.14285714; // constant expression

const int size = n; // not a constant expression

// other code here ...

}

read-only variable pi is a constant expression since it is initialized by a literal.

The following C++ code fragment

int const degree{4};

int poly[degree] {11,12,13,14};

establishes two levels of constancy for variable degree:

  • variable degree cannot be changed during program execution [just as in C], and
  • variable degree is a constant expression and can therefore be used to specify the size of a static array [unlike in C].

Compare the following C program's behavior:

#include <stdio.h>

int main(void) {

int const value = 4;

int array[value] = {1, 2, 3, 4};

printf("%d\n", array[1]);

return 0;

}

with the behavior of this C++ program

#include <iostream>

int main() {

int const value {4};

int array[value] {1, 2, 3, 4};

std::cout << array[1] << "\n";

}

to deduce that a const variable initialized from a constant expression [such as const variable value] is also a constant expression only in C++ code but not in C code.

Clearly, a constant expression has several advantages:

  1. A constant expression can be evaluated at compile-time to save from the CPU cycles required to evaluate the expression at run-time.
  2. A constant expression provides the compiler deep insight into the code so that the compiler can potentially determine additional optimizations.
  3. A constant expression makes the code implicitly thread safe which is difficult to achieve in multi-threaded code.

Whether a given variable or expression is a constant expression depends on the types and initializers:

int square(int n) {

return n*n;

}

int main() {

int const max_files{20}; // max_files is a constant expression

int const limit {max_files}; // limit is a constant expression

int size{27}; // size is not a constant expression

int const sq_size{square(2)}; // sq_size is not a constant expression

// because the value of the initializer is not known until run-time

int array[square(2)]{1,2,3,4}; // illegal because square(2) is not a constant expression

std::cout << array[2] << "\n";

}

Since C+11, the standard has introduced a new keyword that allows programmers to specify that the value of a variable [such as size] or an expression [such square(2)] must be evaluated at compile-time. This keyword when used as a specifier for function square will enable the const variable sq_size to be a constant expression and thus allow the compiler to define the array array with size .

Read Section of the textbook to learn what this C++ keyword is and then write the exact keyword as your answer.

View this question

Recall from HLP1 that internal variables are variables that are defined in function blocks while external variables are variables that are defined outside of any function. By default, internal variables have internal linkage while external variables have external linkage. Internal linkage of a variable implies the compiler doesn't export the variable's name to the linker, while external linkage of a variable implies the compiler will export the variable's name to the linker. Thus, in contrast to internal variables, external variables defined in one source file are accessible to functions defined in other source files. Programmers can force external variables to have internal linkage by adding storage specifier keyword static to the definition of the external variable, as in:

static int global_variable;

In C11, by default, read-only external variables [defined as read-only using type qualifier keyword const] have external linkage. Determine whether such read-only external variables have internal linkage or external linkage in C++ by examining the following C++ code in a source file:

#include <cstdio>

const int foo = 1;

int main() {

std::printf("%d\n", foo);

}

and the following code in a second source file:

const int foo = 4;

Which of the following choices best reflects the output of compilation [only] of each of two source files and the linkage of the corresponding two object files?

0%
0%
0%
0%
0%
View this question

A dynamic array is an array with dynamic storage duration. In C++, a dynamic array is created on the free store using an array new expression. If T is a type and n is a non-negative integral value, expression new T[n] allocates an array of n objects of type T and returns a pointer [which has type T*] to the initial element of the array. If T is a fundamental or built-in type the array elements are left uninitialized. If T is a user-defined type, then each element of the dynamic array is initialized by running its default constructor. As an example,

int *pi {new int[10]};

will allocate memory for an array of int elements and cause pi to point to the first element. Since int is a fundamental type, the new expression doesn't initialize the dynamic array elements. The number of elements doesn’t need to be constant: the size of the array can be determined at runtime, meaning the value between brackets could be a variable rather than a literal.

To deallocate a dynamic array, use the array delete expression. Unlike the array new expression, the array delete expression doesn’t require a length:

delete [] pi;

Like the delete expression, the array delete expression returns void.

Beginner C++ programmers make the common mistake of writing the following incorrect statement to allocate a matrix of rows and columns:

int *p2i {new int[5][6]}; // error

The most efficient solution is to rely on the row-major order followed by C++ [and also by C] to conceptualize a two-dimensional array within the one-dimensional array of bytes comprising physical memory. For example, a two-dimensional array with rows and columns is conceptualized as a row-major ordered one-dimensional array of elements:

int *p2i {new int[5 * 6]};

An element located at the intersection of row and column of the two-dimensional array is accessed by mapping the offset of this two-dimensional element within the one-dimensional array pointed to by p2i:

// offset of two-dimensional element at intersection of row y and column x

int offset = y*6 + x;

Now, read the following code fragment and determine the value written to standard output stream.

int const rows{3}, columns{4};

int *p2i { new int[rows*columns]{7,10,17,12,3,14,-5,16,18,19,11,8} };

int r{1}, c{3};

std::cout << *(p2i+r*columns+c);

View this question

C++ supports dynamic allocation of objects on the free store [or heap] using a new expression that begins with keyword new followed by the type of the object with dynamic storage duration. New expressions create objects of the given type on the free store and return a pointer to the newly minted object. If T is the type of an object, new T is an expression that allocates an uninitialized object of type T on the free store and yields a pointer to this [unnamed] newly allocated object. As an example,

int *p = new int;

will allocate an unnamed and uninitialized object of type int, and cause p to point to that object. It is possible to give a specific value to use when initializing the object by executing the function-style expression new T(args) or the more modern uniform initialization expression new T{args}. As an example,

int *p = new int {42};

will allocate an unnamed object of type int on the free store, initialize the unnamed object to , and cause p to point to that object.

C++ supports deallocation of a dynamic object using a delete expression, which is composed of keyword delete followed by a pointer to an object that was previously allocated by a new expression. A delete expression always returns void. To deallocate the object pointed to by p, you would use the following delete expression:

delete p;

The value contained in memory where the deleted object resided is undefined, meaning the compiler can produce code that leaves anything there. In practice, compilers will try to be as efficient as possible, so typically the object’s memory will remain untouched until the program reuses it for other purposes.

Although new expressions and delete expressions seem to have the same functionality as C standard library functions malloc and free, respectively, they've multiple differences:

  1. For a user-defined type, the new expression not only allocates memory for an object but also calls the object's constructor to initialize the object. On the other hand, function malloc just allocates the appropriate memory for storing the object. Fundamental data types have no constructors, so they will be default initialized meaning that the object is uninitialized and its value is undefined.
  2. The new expression returns the exact type of the allocated object, while function malloc returns a pointer of type void*, so typecasting is required. For example if A

    is an user-defined type, we define an object on the free store like this:

    A *pa {new A};

    whereas the value returned by function

    malloc must be typecast to type A*

    :

    A *pb = static_cast<A*>(malloc(sizeof(A)));

  3. Function malloc signals failure [for example, when the program is out of heap memory] by returning a null pointer where as the new expression throws an exception that requires the programmer to use C++ exception handling mechanisms to avoid program termination.
  4. While function free does nothing more than return the previously allocated memory back to the heap, the delete expression will first call the destructor function of the object's type followed by returning the memory back to the heap.

It should now be clear that C standard library functions malloc, calloc, realloc, and free must not be used in C++ programs except in exceptional cases. Such an exceptional scenario arises when a C++ program interfaces with an API that was implemented in C. Suppose the C++ program makes a call to an API function that returns a pointer pointing to dynamically allocated memory. [which presumably was allocated by the execution of function malloc]. To avoid memory leaks, the dynamically allocated memory must be returned to the free store. In this scenario, the only recourse available to the C++ program is to return the previously allocated dynamic memory back to the free store using C standard library function free.

If the following program doesn't compile, write . If the program has undefined behavior, write . If the program prints unspecified values to standard output, write . Otherwise, write the exact values written to the standard output stream.

#include <iostream>

#include <stdlib.h>

struct Vector {

int x, y, z;

Vector() : x{0}, y{0}, z{0} {} // default constructor

};

int main() {

Vector *pV {static_cast<Vector*>(malloc(sizeof(Vector)))};

std::cout << pV->x << pV->y << pV->z;

free(pV);

}

View this question

nullptr is a keyword introduced in C++11 and is meant to replace the use of both and C standard library macro NULL. Although integer literal  is a valid value to assign to pointers, it is hard for human readers and automated debuggers to detect uses of  in the context of initialization or assignment to pointers. The use of macro NULL is not type safe because the macro typecasts integer literal  to either void* or int depending on the particular compiler.

In contrast to macro NULL or integer literal , keyword nullptr provides a type safe value representing an empty pointer.

Given the following definition

int *pi {nullptr};

choose the best possible answer from the listed choices.

0%
0%
0%
0%
0%
View this question

In C, we can have only a single name to specify an object. For example, the definition 

int n;

associates name n with a block of memory of size bytes. In C++, the concept of references allows us to associate multiple names to an object. For example, the first line of the following code fragment

int i{10};

int *pi{&i};

int &ri{i};

ri = 21;

std::cout << i << ' ' << ri << ' ' << *pi << '\n';

associates name i to an int object. Such a named object is called a variable. The second line defines a pointer variable pi and initializes it with the address of variable i. We can directly refer to the object using name i or indirectly refer to the object using expression *pi. The third definition introduces name ri as a reference to variable i. C++ uses the term reference to mean that name ri is an alias to name i: anywhere you can use name i, you can now use alias ri. That is, the object associated with named i has a second name ri associated with it. Therefore, the code fragment will write  thrice to the standard output stream.

Although it seems that references are similar to pointers, they differ in several essential ways:

  • Pointers can be uninitialized, while references must be initialized [because they must be an alias of something].
  • Once a reference is initialized to alias an object, it cannot be made to alias a different object. On the other hand, pointers can be reassigned to point to another object.
  • There is no such thing as a null reference while we can have a null pointer [by initializing or assigning value nullptr to a pointer variable].

Although every reference must be initialized to refer to something, it is still possible to have a dangling reference. Dangling references arise when a reference is initialized to alias a dynamically allocated object which is then subsequently deleted. Consider the following code fragment:

int *pi = static_cast<int*>(malloc(sizeof(int)));

*pi = 10;

int &ri {*pi}; // ri is now an alias to unnamed object pointed to by pi

ri += 1; // update to 11 the value of object pointed to by pi

free(pi); // the object that pi is pointing to is no longer in existence!!!

// note that the physical memory still exists but not

// the dynamic object that was earlier given storage there

// therefore, ri is now a dangling reference

ri += 2; // undefined behavior

Which of the following code fragments do NOT compile?

0%
0%
View this question

The following code fragment defines a namespace named Lab in a C++ source file:

namespace Lab {

int id;

struct Vector {

int x,y,z;

};

} // notice the lack of a ; character after the closing curly brace!!!

The above code fragment resembles a C/C++ structure definition with both using curly braces to delimit scope and both declaring members, but they are quite different. Keyword struct is used to define an user-defined type that can be instantiated as an object stored in memory. On the other hand, keyword namespace is used to organize names [types, functions, variables] into logical groups to prevent name collisions. For example, namespace Lab adds scope Lab to the names id and Vector declared in that namespace. That is, the names declared in namespace Lab are distinct from similar names declared in both the global scope or in some other namespace such as namespace Lecture:

namespace Lecture {

int id; // this name will not clash with the name id declared in namespace Lab

struct Vector { // ditto

int x,y,z;

};

} // end namespace Lecture

Outside the namespace scope, members can be accessed by specifying a fully qualified name consisting of the namespace's name followed by the scope resolution operator :: and then followed by the member's name. Therefore, type Vector in namespace Lab will be accessed outside the namespace like this: Lab::Vector.

All names at namespace scope are visible to one another without qualification.

What is the output when function foo is invoked?

namespace A {

char c = 'a';

}

char c = 'b';

void foo() {

std::cout << A::c << c;

}

0%
0%
0%
0%
View this question

A structure in C is a user defined data type that can be used to group multiple values under one name. These individual values are called data members and they have different names and possibly different types.

A structure in C++ is a more complex data type that provides facilities to easily implement the concepts of data abstraction and encapsulation [that will be explained in future lectures]. In addition to data members, C++ structures can also contain type declarations and functions. Member functions of a C++ structure can be used by programmers to initialize, to retrieve, and modify the state of the data members of a variable of that structure type. For example, we can define a type Student like this:

#include <string>

struct Student {

std::string name; // string is C++ standard library type declared in <string> that represents a

// variable-length sequence of characters. This class allows us to handle C-style strings

// similar to values of type int

int id;

double gpa;

Student(std::string const& a_name, int an_id, double a_gpa) : name{a_name}, id{an_id}, gpa{a_gpa} { }

void GPA(double new_gpa) { gpa = new_gpa; } // 1st overloaded member function GPA called modifier member function

double GPA() const { return gpa; } // 2nd overloaded member function GPA called accessor member function

};

A member function with the same name as the structure type is called a constructor. The purpose of a constructor is to initialize the data members of an object.

The first overloaded member function GPA is called a modifier because it changes the value of data member gpa. The second overloaded member function GPA is called an accessor because it retrieves the current value of data member gpa.

What is the exact output written to standard output by the following code fragment?

Student s {"Bart", 98, 2.1};

std::cout << s.name << ' ' << s.id << ' ' << s.GPA();

View this question

C++ structures behave differently than C structures. The first difference is that C++ structures enable data abstraction and encapsulation [to be covered in subsequent weeks] by allowing both data [called data members] and functions [called member functions] to be defined inside the structure block.

There is an additional minor difference to know about structure types in C++ in comparison to C. Suppose, we've defined the following structure:

struct Vector {

int x, y, z;

};

Recall that in C, the type defined above is struct Vector and a variable of such a type would be defined like this:

struct Vector vec;

In C++, you can define variables of similar type with or without keyword struct:

struct Vector vec1; // variable of type struct Vector

Vector vec2; // ditto

In practice, this is a superficial difference that leads to a little less typing of C++ code in comparison to C. Now, compile the following code using a C compiler and then a C++ compiler.

#include <stdio.h>

struct Vector {

int x, y, z;

};

int main(void) {

Vector v = {10, 20, 30};

printf("(%d, %d, %d)\n", v.x, v.y, v.z);

return 0;

}

Which of the following best reflects the output of these compilations?

0%
0%
0%
View this question
View this question

Want instant access to all verified answers on distance3.sg.digipen.edu?

Get Unlimited Answers To Exam Questions - Install Crowdly Extension Now!

Browser

Add to Chrome